Dynamically adding UIActionSheet buttons

Every once in a while I come across a seemingly simple iPhone coding requirement, but Apple documentation just doesn’t seem to give enough pointers, and nor does web search throw up anything useful. It may just be my inability to find the right keywords to search for, but it might also be that no-one else has posted anything about it. So here is just one such thing in case it proves helpful to anyone.

The UIActionSheet is a very useful class, and I use it frequently in my apps, but its initialisation method doesn’t allow you to add buttons from an array. Instead you apparently typically just add them hardcoded as an initialiser parameter  – and almost all code examples on the web seem to use this method.

Standard example – hardcoded buttons:

- (void)testActionSheetStatic {
	// Create the sheet with buttons hardcoded in initialiser
	UIActionSheet *sheet = [[UIActionSheet alloc] 
		initWithTitle:@"Static UIActionSheet"
		delegate:self
		cancelButtonTitle:@"Cancel"
		destructiveButtonTitle:nil
		otherButtonTitles:@"Item A", @"Item B", @"Item C", nil];

	[sheet showFromRect:view.bounds inView:view animated:YES];
	[sheet release];
}

So that is all well and good if the option buttons are known in advance and never change. But what if I need to change them at runtime? It seems easy enough to add buttons dynamically instead, by leaving out those button declarations from the initialiser, and adding them afterwards instead, and the following code shows how you might assume this should be done.

Dynamically added buttons (first attempt):

- (void)testActionSheetDynamic {
	// Create the sheet with only cancel button
	UIActionSheet *sheet = [[UIActionSheet alloc] 
		initWithTitle:@"Dynamic UIActionSheet"
		delegate:self
		cancelButtonTitle:@"Cancel"
		destructiveButtonTitle:nil
		otherButtonTitles:nil];

	// Add buttons one by one (e.g. in a loop from array etc...)
	[sheet addButtonWithTitle:@"Item A"];
	[sheet addButtonWithTitle:@"Item B"];
	[sheet addButtonWithTitle:@"Item C"];

	[sheet showFromRect:view.bounds inView:view animated:YES];
	[sheet release];
}

The problem with this becomes apparent when you run it – the cancel button appears at the TOP of the sheet, whereas standard practice seems to be that it should be at the bottom. How to achieve this? I didn’t manage to find a way of doing this while adding the cancel button in the initialiser. Instead I eventually found a way of doing so by adding it also as a dynamic button.

Dynamically added buttons (also with dynamic cancel button):

- (void)testActionSheetDynamic {
	// Create the sheet without buttons
	UIActionSheet *sheet = [[UIActionSheet alloc] 
		initWithTitle:@"Dynamic UIActionSheet"
		delegate:self
		cancelButtonTitle:nil
		destructiveButtonTitle:nil
		otherButtonTitles:nil];

	// Add buttons one by one (e.g. in a loop from array etc...)
	[sheet addButtonWithTitle:@"Item A"];
	[sheet addButtonWithTitle:@"Item B"];
	[sheet addButtonWithTitle:@"Item C"];

	// Also add a cancel button
	[sheet addButtonWithTitle:@"Cancel"];
	// Set cancel button index to the one we just added so that we know which one it is in delegate call
	// NB - This also causes this button to be shown with a black background
	sheet.cancelButtonIndex = sheet.numberOfButtons-1;

	[sheet showFromRect:view.bounds inView:view animated:YES];
	[sheet release];
}

In this case the cancel button appears at the bottom, and all works as expected.

The main remaining question I had was what on earth the destructive button was (also apparently not explained in Apple documentation). Some experimentation seemed to show that it was effectively identical to a cancel button with the exception that it had RED background instead of a black one. So if you change the last example to set the destructiveButtonIndex instead of the cancelButtonIndex, then the only difference is that the button labelled “Cancel” would have a red background.

For completeness and sanity, this is the delegate code that goes with all of the above examples.

- (void)actionSheet:(UIActionSheet *)actionSheet 
		clickedButtonAtIndex:(NSInteger)buttonIndex {
	if (buttonIndex == actionSheet.cancelButtonIndex) { return; }
	switch (buttonIndex) {
		case 0:
		{
			NSLog(@"Item A Selected");
			break;
		}
		case 1:
		{
			NSLog(@"Item B Selected");
			break;
		}
		case 2:
		{
			NSLog(@"Item C Selected");
			break;
		}
	}
}

6 thoughts on “Dynamically adding UIActionSheet buttons”

  1. [sheet addButtonWithTitle:@”Cancel”];
    sheet.cancelButtonIndex = sheet.numberOfButtons-1;

    should be written as

    sheet.cancelButtonIndex = [sheet addButtonWithTitle:@”Cancel”];

  2. You miss the part how to deal with the buttonindex. Your sample assumes all 3 buttons will show, but since it is ‘dynamic’ i.e. button B might not be there the buttonindex shifts and your switch statement is wrong.

  3. For an fun experience add about 10 buttons.

    You’ll get an interesting surprise… 😉

    Don’t worry. It won’t hurt anything. It’s just not what you would expect.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s