25
votes

I have discovered a strange problem when using UIActionSheet on the iPhone (iOS 4.2). Consider this code:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

    [self.window addSubview:viewController.view];
    [self.window makeKeyAndVisible];

    UIActionSheet *actionSheet = [[UIActionSheet alloc] 
                                  initWithTitle:@"TestSheet" 
                                  delegate:self 
                                  cancelButtonTitle:@"Cancel" 
                                  destructiveButtonTitle:nil 
                                  otherButtonTitles: nil];

    [actionSheet addButtonWithTitle:@"one"];
    [actionSheet addButtonWithTitle:@"two"];
    [actionSheet addButtonWithTitle:@"three"];
    [actionSheet addButtonWithTitle:@"four"];
    [actionSheet addButtonWithTitle:@"five"];
    [actionSheet addButtonWithTitle:@"six"];
    //uncomment next line to see the problem in action
    //[actionSheet addButtonWithTitle:@"seven"];

    [actionSheet showInView:window];
    [actionSheet release];

    return YES;
}
- (void) actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
    NSLog(@"buttonIndex: %d, cancelButtonIndex: %d, firstOtherButtonIndex: %d",
          buttonIndex, 
          actionSheet.cancelButtonIndex, 
          actionSheet.firstOtherButtonIndex);
}

If you start this application, the actionsheet behaves as expected. That means the cancelButtonIndex is always 0, and the button indexes are reported correctly. 1 for button "one" and so on. If you comment in the line for adding the seventh button, the actionsheet produces a sort of tableview, with the cancel button on an extra line. If I press the "one" button in this case, the buttonindex variable is 0, but so is the cancelButtonIndex. It is impossible to tell if the user has tapped the "cancel" or the "one" button. That doesn't seem like it should be this way. Does anyone disagree? Thanks for your help.

5
Six buttons on action sheet? Really? I'd suggest using a table for options...Eimantas
Well what's the point of having an actionsheet which can handle more than 6 buttons in a scrollview if you're not supposed to use it?huesforalice
Had the same problem, it's a bug apparentlyEge Akpinar
Amazing that this bug -- and it clearly is a bug -- still exists as of iOS 7.0+.Christopher King

5 Answers

34
votes

I ran into the same issue even though I already was including the Cancel Button as the last one in the action sheet and setting its index accordingly. My problems had to do with the 'Destructive' button. After some investigation, here is my take on the problem:

  • After N buttons have been added to the actionsheet, it switches its layout to put the Destructive button at the top and the Cancel button at the bottom. In between is a scrollable view that includes all of the other buttons. Other sources indicate that this is a a table view.

  • N is 7 for Portrait orientation and 5 for Landscape orientation. N is 9 for Portrait orientation on larger, 4" screen. These numbers are for all buttons including Cancel and Destructive. To be clear, N is the largest number of buttons before the switch. N+1 buttons causes the UIActionSheet to switch to the scrollable view.

  • It does not matter where in the action sheet you had originally put the Cancel and Destructive buttons within the action sheet. Once the limit has been reached, the Destructive button is moved to the top and the Cancel is moved to the bottom.

  • The problem is that the indices are not adjusted accordingly. So, if you did not initially add the Cancel as the last button and the Destructive as the first, the wrong index will be reported in actionSheet:clickedButtonAtIndex: as the initial report stated.

  • So, if you are going to have more than N buttons in your action sheet you MUST add the Destructive button to the actionSheet as the first button to the action sheet. You MUST add the Cancel button as the last button added to the action sheet. When initially constructing the sheet just leave both as nil, as described in another answer.

13
votes

I've just had this problem. Solve it by not setting the cancel button initially. I set the buttons individually something like this:

   for(int index = 0; index < buttonTotal; index++)
   {
    [actionSheet addButtonWithTitle:[NSString stringWithFormat:buttonText, [buttonItems objectAtIndex: index]]];
   }

   [actionSheet addButtonWithTitle:@"Cancel"];
   actionSheet.cancelButtonIndex = actionSheet.numberOfButtons;

I believe the zero index is used by the destructiveButton if you use it so the other buttons will increment from there, otherwise they will start from 0.

Not sure I agree with the table option since above a certain amount, the buttons default to a scrollable list.

1
votes

file a bug about the problem. Include a little sample project and wait for some months to hear back from them.

For now you can set up your buttons statically in the init

UIActionSheet *actionSheet = [[UIActionSheet alloc] 
                              initWithTitle:@"TestSheet" 
                              delegate:self 
                              cancelButtonTitle:@"Cancel" 
                              destructiveButtonTitle:nil 
                              otherButtonTitles: @"one", @"two", @"three", @"four", @"five", @"six", @"seven", nil];

works without problems.

1
votes

1) Instantiate the actionview using

[[UIActionSheet alloc] InitWithTitle:delegate:cancelButtonTitle:destructiveButtonTitle:OtherButtonTitles:]

without either the cancel button or destructive button (set them to nil)

2) Add all of the buttons as normal, using [myActionSheet addButtonWithTitle:(NSString *)].

3) If you want a special button, add it using the same method as step 2, and set the title to whatever (@"Cancel", for instance);

4) Now set the property [myActionSheet setCancelButtonIndex:] to the index of the last button on the actionsheet, which was the cancel button made. Do the same for a destructive button. (These are -1 by default, which causes them not to be shown)

  • Note that a destructive button will always appear on top, and a cancel button will always appear on bottom, that cannot be changed.

  • Also, you can certainly add the cancel/destructive button at index 0, and add all of the other buttons after that. However now the first index of your other buttons is 1, instead of zero. This can be confusing if you have an array that corresponds with the alertview buttons.

0
votes

Do like as below:

UIActionSheet *actionSheet = [[UIActionSheet alloc] 
                              initWithTitle:@"TestSheet" 
                              delegate:self 
                              cancelButtonTitle:@"Cancel" 
                              destructiveButtonTitle:@"Delete" 
                              otherButtonTitles: @"one", @"two", @"three", @"four", @"five", @"six", @"seven", @"eight", nil];

It means that define the destructive button as destructiveButtonTitle. AVOID using destructive/cancel button in the otherButtonTitles.