Building a Dynamic UIActionSheet

Most of the examples I see for UIActionSheet (the iOS pop-up menu) show static choices and checking for which one was picked by using constant values. That's not going to work if your menu needs to be dynamic.

For example, if you want to have an option to Email or Print an item, you might want this on a UIActionSheet menu. But if either of these things aren't available (for example, you may be running on a device that doesn't support printing, or there may not be an email account configured) then you shouldn't show these items.

Here's one way to do this.

Start out by creating localized string variables for the menu items.

[cc lang="objc"]
NSString *MPActionMenuCancelItem = NSLocalizedString(@"Cancel", @"Cancel item in Action Sheet");
NSString *MPActionMenuDeleteItem = NSLocalizedString(@"Delete", @"Delete item in Action Sheet");
NSString *MPActionMenuEmailItem = NSLocalizedString(@"Email", @"Email item in Action Sheet");
NSString *MPActionMenuPrintItem = NSLocalizedString(@"Print", @"Print item in Action Sheet");
[/cc]

Note that I'm using NSLocalizedString here and not simply using a string value.

Now you need to dynamically build the UIActionSheet. Here's code to do that:

[cc lang="objc"]
- (IBAction)actionSelected:(id)sender
{
UIActionSheet *actions = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];

if ([MFMailComposeViewController canSendMail])
[actions addButtonWithTitle:MPActionMenuEmailItem];

if ([UIPrintInteractionController canPrintURL:[NSURL fileURLWithPath:imageData.path]])
[actions addButtonWithTitle:MPActionMenuPrintItem];

actions.destructiveButtonIndex = [actions addButtonWithTitle:MPActionMenuDeleteItem];
actions.cancelButtonIndex = [actions addButtonWithTitle:MPActionMenuCancelItem];

[actions showFromBarButtonItem:sender animated:YES];
}
[/cc]

Notice how email and printing are only included if supported.

It's convenient that addButtonWithTitle: returns the button index that was added, so we can assign the button indexes for those directly.

When the user picks an item from the UIActionSheet, we need to figure out what they picked and act on it.

[cc lang="objc"]
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == actionSheet.cancelButtonIndex)
return;

if (buttonIndex == actionSheet.destructiveButtonIndex)
[self deleteItem];

NSString *clicked = [actionSheet buttonTitleAtIndex:buttonIndex];
if ([clicked compare:MPActionMenuEmailtem] == NSOrderedSame)
[self emailItem];

if ([clicked compare:MPActionMenuPrintItem] == NSOrderedSame)
[self printItem];
}
[/cc]

I thought about building a table of indices to action values or something more complex like that, because comparing the selected string somehow just doesn't feel right. That may be my years as a Windows developer showing through, because this way is straightforward, and works great.