Duck
Duck

Reputation: 35953

Elegant solution for UIActionSheet mess

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?

Upvotes: 4

Views: 3364

Answers (4)

Can
Can

Reputation: 8571

Old question, but with iOS 8, the new UIAlertController class, allows you to create UIAlertActions using blocks:

let alertController = UIAlertController(title: "Pancakes", message: "Do you like them with:", preferredStyle: .ActionSheet)

alertController.addAction(UIAlertAction(title: "Maple Syrup", style: .Default, handler: { (alert) -> Void in
    println("Picked syrup")
}))

alertController.addAction(UIAlertAction(title: "Jam", style: .Default, handler: { (alert) -> Void in
    println("Jam? eh?")
}))

alertController.addAction(UIAlertAction(title: "I HATE PANCAKES", style: .Destructive, handler: { (alert) -> Void in
    println("You monster.")
}))

Upvotes: 0

AliSoftware
AliSoftware

Reputation: 32681

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 */
})();

Upvotes: 5

Shai Mishali
Shai Mishali

Reputation: 9382

Sorry for adding a late answer for such an old question, but googling "UIActionSheet blocks" leads here so I thought I'd share with you this easy block implementation I wrote.

https://github.com/freak4pc/UIActionSheet-Blocks

Cheers!

Upvotes: 0

rooster117
rooster117

Reputation: 5552

I prefer to help (although not fully solve) this problem by using an array of values. It makes it so that you at least have a unified source of data. So by creating an array for your values in your viewDidLoad or whatever and when you create your action sheet you can change it to something like:

//where myValuesArray is an array of strings you declared in viewDidLoad
//myValuesArray = [NSArray arrayWithObjects:@"aaa", @"bbb", @"ccc", @"ddd", nil];

UIActionSheet * myChoices = [[UIActionSheet alloc]
    initWithTitle:nil
    delegate:self
    cancelButtonTitle:@"cancel"
    destructiveButtonTitle:@"erase"
    otherButtonTitles:nil];

for(NSString *title in myValuesArray)
{
    [myChoices addButtonWithTitle:@"LMNOP"];
}

Then in your clickedButtonAtIndex you are checking against the index of the exact same array with what I assume is your choices. With this you can also just update the array when you want to make changes. I hope this helps.

Upvotes: 3

Related Questions