UIActionSheet
cancel button
iOS development
user interface
bug fix

UIActionSheet cancel button strange behaviour

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

UIActionSheet is legacy UIKit API, but many older iOS codebases still contain it. The cancel button often looks strange because its behavior depends on presentation style, delegate handling, and special UIKit dismissal rules. Most bugs come from treating cancel like an ordinary action button when UIKit does not.

Why the Cancel Button Behaves Differently

In UIActionSheet, the cancel button is the safe way out of the sheet. UIKit gives it special treatment, which affects:

  • visual placement
  • dismissal by tapping outside the sheet
  • delegate callbacks
  • button indexing

On iPhone, the cancel button is commonly separated from the main actions. On iPad, sheet presentation through popovers changes the interaction model again, which can make the button feel inconsistent if the presentation anchor is wrong.

The Legacy Setup Pattern

Most old Objective-C code creates a sheet with a delegate, a destructive button, normal buttons, and a dedicated cancel title.

objective-c
1UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"Choose an action"
2                                                   delegate:self
3                                          cancelButtonTitle:@"Cancel"
4                                     destructiveButtonTitle:@"Delete"
5                                          otherButtonTitles:@"Archive", @"Share", nil];
6
7[sheet showInView:self.view];

That code is valid, but it immediately creates one trap: the cancel button index should be read from the object, not guessed manually.

Use the Exposed Button Indexes

Hard-coded numeric indexes are the most common cause of cancel button bugs. A new button gets inserted, but old switch logic stays in place and now the cancel button triggers the wrong branch.

objective-c
1- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
2{
3    if (buttonIndex == actionSheet.cancelButtonIndex) {
4        return;
5    }
6
7    if (buttonIndex == actionSheet.destructiveButtonIndex) {
8        [self deleteItem];
9        return;
10    }
11
12    NSString *title = [actionSheet buttonTitleAtIndex:buttonIndex];
13
14    if ([title isEqualToString:@"Archive"]) {
15        [self archiveItem];
16    } else if ([title isEqualToString:@"Share"]) {
17        [self shareItem];
18    }
19}

This is much safer than assuming, for example, that cancel is always the last index in every configuration.

Cancellation Is Not Always a Button Tap

Another source of confusion is that cancellation can happen without a direct press on the cancel button. Depending on platform and presentation style, tapping outside the sheet can dismiss it as a cancellation event.

That is why the delegate also exposes a cancellation callback:

objective-c
1- (void)actionSheetCancel:(UIActionSheet *)actionSheet
2{
3    NSLog(@"Action sheet was cancelled");
4}

If your code only uses clickedButtonAtIndex:, you can miss cancellation paths that still matter to the surrounding workflow.

iPad Presentation Issues

On iPad, UIActionSheet was often shown from a bar button item or anchored rectangle rather than simply sliding up from the bottom. If that anchor is wrong, the behavior can look like a cancel button problem even though the real problem is presentation.

objective-c
[sheet showFromBarButtonItem:self.navigationItem.rightBarButtonItem animated:YES];

A badly anchored sheet can lead to confusing dismissal behavior, especially in old split-view or popover-heavy code.

The Modern Replacement Is Better

UIActionSheet is deprecated. UIAlertController with action-sheet style avoids most of the old indexing and delegate problems because each action carries its own handler.

objective-c
1UIAlertController *controller =
2    [UIAlertController alertControllerWithTitle:@"Choose an action"
3                                        message:nil
4                                 preferredStyle:UIAlertControllerStyleActionSheet];
5
6[controller addAction:[UIAlertAction actionWithTitle:@"Archive"
7                                               style:UIAlertActionStyleDefault
8                                             handler:^(UIAlertAction *action) {
9    [self archiveItem];
10}]];
11
12[controller addAction:[UIAlertAction actionWithTitle:@"Delete"
13                                               style:UIAlertActionStyleDestructive
14                                             handler:^(UIAlertAction *action) {
15    [self deleteItem];
16}]];
17
18[controller addAction:[UIAlertAction actionWithTitle:@"Cancel"
19                                               style:UIAlertActionStyleCancel
20                                             handler:nil]];
21
22[self presentViewController:controller animated:YES completion:nil];

This removes most of the old maintenance burden around button ordering.

When You Still Need to Debug Legacy Code

If an old app still uses UIActionSheet, check these questions first:

  • are button indexes derived from the sheet or hard-coded
  • is cancel being handled separately from normal actions
  • is dismissal happening through actionSheetCancel:
  • is the sheet being presented correctly for the device

Most strange cancel behavior falls into one of those buckets.

Common Pitfalls

  • Hard-coding the cancel button index instead of reading cancelButtonIndex.
  • Treating cancellation as if it were just another action selection.
  • Ignoring actionSheetCancel: and missing non-button dismissal flows.
  • Debugging iPad presentation bugs as if they were delegate logic bugs.
  • Continuing to extend UIActionSheet instead of migrating to UIAlertController.

Summary

  • The cancel button in UIActionSheet has special UIKit behavior and should not be treated like a normal action.
  • Use cancelButtonIndex and destructiveButtonIndex instead of manual numeric assumptions.
  • Cancellation can happen through dismissal, not only through an obvious cancel tap.
  • iPad anchoring and presentation style can make the issue look worse than it is.
  • For modern code, replace UIActionSheet with UIAlertController and avoid the legacy quirks.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.