Distinguishing Between Multiple UIActionSheets with Swift

The challenge when dealing with the presentation and handling of more than one UIActionSheet in a single View Controller is made clear by asking, “How am I going to tell which action sheet I’m dealing with so that I can handle the user’s choice appropriately?”

Presumably, the user’s interaction with one of the action sheets will be different than the other(s), so you’ll need to think through how to distinguish between them, in order to respond to that interaction appropriately.

I faced such a scenario in a recent project, and I thought I’d share my solution. Check out my GitHub example to dive in if you prefer learning by exploration!

Using UIView Tags

Essentially, I chose to make use of the tag property, which all UIView subclasses inherit. The UIView Class Reference documentation specifies that the tag property can be used to identify the view at runtime:

You can set the value of this tag and use that value to identify the view later.

Example

Here’s a quick example showing the setting of this property so that the action sheet can be differentiated when it comes time to handle the user’s choice:

1let actionSheet1 = UIActionSheet()
2        actionSheet1.tag = 0
3        // set other properties, such as delegate, as well as buttons...
4        
5        let actionSheet2 = UIActionSheet()
6        actionSheet2.tag = 1
7        // set other properties, such as delegate, as well as buttons...

Better Solution in Swift?

Simple enough, right? Here’s my only problem with the above implementation if I’m using Swift: We have language features available to us that allow us to avoid setting the tag property to the integer value in-line like I did in this code snippet.

Here, instead of assigning in-line, I’m going to refactor and employ a Swift enumeration to help name the tag. Underneath, there will still be integers involved, but my goal in using an enumeration is two-fold:

  • Identify the action sheet in code with a name. This should help my code be more coherent and readable.
  • Encapsulate a single source of truth for the tag values and avoid “magic integers” in my code. While the integers assigned to the tags can be arbitrary, if they ever do need to be changed, I change the enumeration, rather than changing each place where the tag is set or checked to perform branching logic.

The arguments are fundamentally the same as those I made when I wrote about replacing magic strings in Swift and implement precisely the same strategy.

Refactored Example

The refactored version of the code snippet previously presented could look like this, then:

 1enum ActionSheetTag: Int {
 2            // Integer values will be implicitly supplied; you could optionally set your own values
 3            case ActionSheet1
 4            case ActionSheet2
 5        }
 6        
 7        let actionSheet1 = UIActionSheet()
 8        actionSheet1.tag = ActionSheetTag.ActionSheet1.toRaw()
 9        // set other properties, such as delegate, as well as buttons...
10        
11        let actionSheet2 = UIActionSheet()
12        actionSheet2.tag = ActionSheetTag.ActionSheet2.toRaw()
13        // set other properties, such as delegate, as well as buttons...

It’s worth noting that rather than using an enumeration, I could have chosen to define a couple of constants at a scope visible to both my setting of the tag, and the conditional logic I’d use in my UIActionSheetDelegate callback. The end goal and result would be the same: Clarity, achieved by assigning names to the tags, and the avoidance of “magic integers” appearing in my code.

Wrapping Up – UIActionSheetDelegate Implementation

To see how to perform the conditional logic needed in the UIActionSheetDelegate callback method, take a look at this final code snippet:

 1func actionSheet(actionSheet: UIActionSheet, clickedButtonAtIndex buttonIndex: Int) {
 2        if let tag = ActionSheetTag.fromRaw(actionSheet.tag) {
 3            switch tag {
 4            case .ActionSheet1:
 5                handleActionSheet1Interaction() // Function definition omitted for brevity, but ideally you'd implement a function to do something in response to the user's interaction with ActionSheet 1
 6            case .ActionSheet2:
 7                handleActionSheet2Interaction() // // Function definition omitted for brevity, but ideally you'd implement a function to do something in response to the user's interaction with ActionSheet 2
 8            default:
 9                println("Unknown action sheet.")
10            }
11        }
12    }

Summary

I’ve done it before, but by using Swift enumerations again in a new context, I was able to provide better clarity to the intention of my code when I had multiple UIActionSheet instances that I needed to handle in a single view controller. In addition to this clarity, I also encapsulated a single source of truth for identifying my UIActionSheet instances, rather than placing “magic integers” throughout my code. Have a look at (and play with) the example I’ve put up on GitHub for further study and improvement!

comments powered by Disqus