I have an action sheet with options that vary depending on the circumstances. There are enough different button titles that I would like to construct an array of those button titles first, but I can't figure out how to convert that into the varargs format.
I want to do something like this:
NSMutableArray *buttonTitles = [NSMutableArray array];
if (condition1) {
[buttonTitles addObject: @"Do action 1"];
}
if (condition2) {
[buttonTitles addObject: @"Do action 2"];
}
if (condition3) {
[buttonTitles addObject: @"Do action 3"];
}
if (condition4) {
[buttonTitles addObject: @"Do action 4"];
}
UIActionSheet *actionSheet = [[[UIActionSheet alloc] initWithTitle: nil delegate: self cancelButtonTitle: @"Cancel" destructiveButtonTitle: nil otherButtonTitles: buttonTitles] autorelease];
Now obviously if I had to I could do something like this instead:
UIActionSheet *actionSheet = nil;
if (condition1 && condition2 && condition3 && condition4) {
actionSheet = [[[UIActionSheet alloc] initWithTitle: nil delegate: self cancelButtonTitle: @"Cancel" destructiveButtonTitle: nil otherButtonTitles: @"Do action1", @"Do action2", @"Do action3", @"Do action 4", nil] autorelease];
} else if (condition1 && condition2 && condition3 && !condition4) {
actionSheet = [[[UIActionSheet alloc] initWithTitle: nil delegate: self cancelButtonTitle: @"Cancel" destructiveButtonTitle: nil otherButtonTitles: @"Do action1", @"Do action2", @"Do action3", nil] autorelease];
}
// else ...
// about 14 other cases!
But that would be horrible. Anyone know some nice syntactic sugar to help me out?
EDIT:
It has been suggested that I use addButtonWithTitle
, which on the face of it looks great, unfortunately it this puts the additional buttons after the cancel button, which isn't desirable.
I believe this is bug with Apple's code since their documentation on addButtonWithTitle
states:
// adds a button with the title. returns the index (0 based) of where it was added. buttons are displayed in the order added except for the
// destructive and cancel button which will be positioned based on HI requirements. buttons cannot be customized.
HI requirements (Apple's own Human Interface guidelines) favor the cancel button below all other options, so I'd say Apple screwed up. Of course, that doesn't really help me, so I'm back to trying to convert between an NSArray and a varargs, which I still don't know how to do.
You can try this
NSMutableArray *buttonTitles = [NSMutableArray array];
if (condition1) {
[buttonTitles addObject: @"Do action 1"];
}
if (condition2) {
[buttonTitles addObject: @"Do action 2"];
}
if (condition3) {
[buttonTitles addObject: @"Do action 3"];
}
if (condition4) {
[buttonTitles addObject: @"Do action 4"];
}
[buttonTitles addObject: @"Cancel"];
UIActionSheet *actionSheet = [[[UIActionSheet alloc] initWithTitle: nil delegate: self cancelButtonTitle: nil destructiveButtonTitle: nil otherButtonTitles: nil] autorelease];
for (NSString *title in buttonTitles) {
[actionSheet addButtonWithTitle: title];
}
[actionSheet setCancelButtonIndex: [buttonTitles count] - 1];
A variation on Himanshu's answer:
UIActionSheet * as = [[UIActionSheet alloc] initWithTitle:@"Share"
delegate:self
cancelButtonTitle:nil /* don't set Cancel title here! */
destructiveButtonTitle:nil
otherButtonTitles:nil];
int cancelButtonIndex = 0; // will remain 0 if no other buttons besides "Cancel"
if ( canShareBySMS )
{
[as addButtonWithTitle:kSMSButtonText];
cancelButtonIndex++;
}
if ( canShareByMail )
{
[as addButtonWithTitle:kEmailButtonText];
cancelButtonIndex++;
}
/* etc. */
// after all other buttons have been added, include Cancel
[as addButtonWithTitle:@"Cancel"];
[as setCancelButtonIndex:cancelButtonIndex];
[as showInView:self.sharingViewController.view];
[as release];
Note: your UIActionSheetDelegate method should check against button titles instead of buttonIndex:
- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex {
if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:kSMSButtonText]) {
//share via sms
}else if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:kEmailButtonText]) {
//share via mail
}
}
I think the key detail people miss is setting the cancelButton in the initWithTitle selector, instead of adding it after all the other buttons and specifying the look of the cancel button by calling setCancelButtonIndex:.
Why not just do something like this:
for (Nstring *button in buttonTitles)
[actionSheet addButtonWithTitle:button];
Here is a method I tried that seems to work just fine. This works if you want to add an additional button at the end of the buttons, before the "cancel" button.
NSString * optionalButton = nil;
if (condition4) {
optionalButton = @"Some Additional Option";
}
UIActionSheet * actionSheet = [[UIActionSheet alloc] initWithTitle:@"Some Title"
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Button 1",@"Button 2",@"Button 3",optionalButton, nil];
[actionSheet showInView:self.view];
It appears to be okay to add an extra 'nil' at the end of the buttons list if your condition (condition4) is not met.
I've successfully tested this on iOS7.