Elegant solution for UIActionSheet mess

ios, ipad, iphone

Solution

Since I created a block-based version of `UIAlertView` and `UIActionSheet`, I personally never use the delegate-based Apple version again. You can download my `OHActionSheet` and `OHAlertView` classes in my GitHub repository.

Because they are based on the completionBlock pattern, they are more readable (all the code is at the same place, no common delegate for multiple `UIActionSheets`, …), and more powerful (because blocks also capture their context as needed).

NSArray* otherButtons = @[ @"aaa", @"bbb", @"ccc", @"ddd" ];
[OHActionSheet showSheetInView:self.view
                         title:nil
             cancelButtonTitle:@"cancel"
        destructiveButtonTitle:@"erase"
             otherButtonTitles:otherButtons
        completion:^(OHActionSheet* sheet, NSInteger buttonIndex)
 {
   if (buttonIndex == sheet.cancelButtonIndex) {
     // cancel
   } else if (buttonIndex == sheet.destructiveButtonIndex) {
     // erase
   } else {
     NSUInteger idx = buttonIndex - sheet.firstOtherButtonIndex;
     // Some magic here: thanks to the blocks capturing capability,
     // the "otherButtons" array is accessible in the completion block!
     NSString* buttonName = otherButtons[idx];
     // Do whatever you want with idx and buttonName
   }
 }];

Additional Note: how to `switch/case` on NSStrings

Note that in the otherButtons part of the `if/else` test in your completion handler, you can then either test for the `idx` using a `switch/case`, or use my `ObjcSwitch` category, that will allow you to write `switch/case`-like code but for `NSStrings`, so you can have a code like this in your `OHActionSheet`'s completion handler:

NSUInteger idx = buttonIndex - sheet.firstOtherButtonIndex;
NSString* buttonName = otherButtons[idx];
[buttonName switchCase:
   @"aaa", ^{ /* Some code here to execute for the "aaa" button */ },
   @"bbb", ^{ /* Some code here to execute for the "bbb" button */ },
   @"ccc", ^{ /* Some code here to execute for the "ccc" button */ },
   ..., nil
];

EDIT : Now that the latest LLVM compiler supports the new "Object Literals" syntax, you can do the same as `ObjcSwitch` using the compact syntax of an NSDictionary:

((dispatch_block_t)@{
   @"aaa": ^{ /* Some code here to execute for the "aaa" button */ },
   @"bbb": ^{ /* Some code here to execute for the "bbb" button */ },
   @"ccc": ^{ /* Some code here to execute for the "ccc" button */ },
}[buttonName] ?:^{
       /* Some code here to execute for defaults if no case found above */
})();

Problem

I am trying to find an elegant solution to UIActionSheet problem. I use UIActionSheets like this: ``` UIActionSheet * myChoices = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"cancel" destructiveButtonTitle:@"erase" otherButtonTitles: @"aaa", @"bbb", @"ccc", @"ddd", nil]; ``` the problem is that in order to discover the option selected by the user, I have to use this: ``` - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { switch ([actionSheet tag]) { case 0: ... case 1: ... case 2: ... case 3: ... } } ``` this case based on index is terrible, because if I change the order of the aaa, bbb, ccc, etc., on the action sheet I have to change the case order. This index stuff is not good as a solid solution. I have tried to imagine a way to do that and become index independent but did not come any satisfactory solution. Using buttonTitleAtIndex is not good enough too, because my apps are localized and I would have to test for n titles for every entry. Any suggestions?

Original source