9

I've subclassed UIActionSheet, and in the -init method, I have to add the buttons individually after calling the super init (can't pass a var_args).

Right now, it looks like this:

if (self = [super initWithTitle:title delegate:self cancelButtonTitle:cancel destructiveButtonTile:destroy otherButtonTitles:firstButton,nil]) {
  if (firstButton) {
    id buttonTitle;
    va_list argList;
    va_start(argList, firstButtton);
    while (buttonTitle = va_arg(argList, id)) {
      [self addButtonWithTitle:buttonTitle]
    }
    va_end(argList);
  }
}
return self;

However, my specific use in this case has no destructive button, a cancel button, and four other buttons. When it shows up, the ordering is all off, showing up as

Button1
Cancel
Button2
Button3

Like they were simply added to the end of the list, which makes sense; however, I don't WANT it to look like this; so what do I do? Is there, in fact, any way to subclass UIActionSheet correctly and make this work?

Ed Marty
  • 39,590
  • 19
  • 103
  • 156

3 Answers3

21

You can just add them in your correct order, and then set the cancelButtonIndex and destructiveButtonIndex manually.

For your code example:

if (self = [super initWithTitle:title delegate:self cancelButtonTitle:nil destructiveButtonTile:nil otherButtonTitles:nil]) {
  if (firstButton) {
    id buttonTitle;
    int idx = 0;
    va_list argList;
    va_start(argList, firstButtton);
    while (buttonTitle = va_arg(argList, id)) {
      [self addButtonWithTitle:buttonTitle]
      idx++;
    }
    va_end(argList);
    [self addButtonWithTitle:cancel];
    [self addButtonWithTitle:destroy];
    self.cancelButtonIndex = idx++;
    self.destructiveButtonIndex = idx++;
  }
}
return self;
Aviad Ben Dov
  • 6,351
  • 2
  • 34
  • 45
8

Aviad Ben Dov's answer is correct, however the button index counter is not needed to set the index for the destroy and cancel indices. The addButtonWithTitle: method returns the index of the newly used button so we can use that value right away like so:

    if (self = [super initWithTitle:title delegate:self cancelButtonTitle:nil destructiveButtonTile:nil otherButtonTitles:nil]) {
  if (firstButton) {
    id buttonTitle;
    va_list argList;
    va_start(argList, firstButtton);
    while (buttonTitle = va_arg(argList, id)) {
      [self addButtonWithTitle:buttonTitle]
    }
    va_end(argList);
    self.cancelButtonIndex = [self addButtonWithTitle:cancel];
    self.destructiveButtonIndex = [self addButtonWithTitle:destroy];
  }
}
return self;
Brad The App Guy
  • 16,255
  • 2
  • 41
  • 60
3

The earlier answers cause the destructive button to be placed at the bottom, which is not in accordance with the HIG, and which is also very confusing for the user. The destructive button should be at the top, the cancel on the bottom, and the others in the middle.

The following orders them correctly:

sheetView         = [[UIActionSheet alloc] initWithTitle:title delegate:self
                                       cancelButtonTitle:nil destructiveButtonTitle:destructiveTitle otherButtonTitles:firstOtherTitle, nil];
if (otherTitlesList) {
    for (NSString *otherTitle; (otherTitle = va_arg(otherTitlesList, id));)
        [sheetView addButtonWithTitle:otherTitle];
    va_end(otherTitlesList);
}
if (cancelTitle)
    sheetView.cancelButtonIndex      = [sheetView addButtonWithTitle:cancelTitle];

See https://github.com/Lyndir/Pearl/blob/master/Pearl-UIKit/PearlSheet.m for an implementation (a UIActionSheet wrapper with a block-based API).

lhunath
  • 120,288
  • 16
  • 68
  • 77