Elegant UITableView row selection behavior based on edit state
If you’re a Cocoa Touch developer, you’ve undoubtedly implemented -[UITableViewDelegate tableView:didSelectRowAtIndexPath:]
a few hundred times. I did this recently and needed different behavior based on whether the table view was in its editing state (whether -[UITableView isEditing]
). In this particular case, the best solutions I could figure out were variants of nested conditionals, which are just ugly.
Then I stumbled on something else which, depending on your use case, may be more elegant. Take a look, then I’ll go into more detail. (For purposes of this example, assume CDZType
is a typedef’d enum containing two values: CDZTypePhone, CDZTypeEmail
.) I’ve added an important update about how to define this enum at the end of this post.
In this case, I wanted to take some action when (a phone or email row was selected and the table view was NOT in edit mode) or (a phone row was selected AND the table view was in edit mode). This can obviously be accomplished by nesting conditionals or writing complex conditional statements, but I didn’t like the style of either of those choices (and there’s a lot more to this code in our app).
This solution is really simple.
I take advantage of the fact that there are very few values in this enum, and of the fact that C enum values are integers. I create a bitmask which is simply the integer 2^9. It seems heavy-handed, but the bitmask must be #define
d; the compiler insists that it must know all values which will be used in case statements, so a static NSUInteger
won’t work.
If the table view is in editing mode, I bitwise-OR the row type enum with the bitmask and switch on that value; otherwise I bitwise-OR the row type with 0 (which doesn’t change the value). Then I just switch on that slightly-modified integer!
A few additional notes:
- This strategy will obviously cause problems if there are ever 2^9 values in the enum; in this case I am guaranteed that will never happen. If you do get up to 2^9 (If you’re paranoid, you might add a final “count” value to the enum, and use a quick preprocessor rule in this method to break the build if that count ever exceeds 2^9.)
- If you ever hit 2^9 values, change the bitmask to
1 << 10
. - Note that I’ve taken care to namespace that bitmask, so that it won’t conflict with anything after the preprocessor handles this file..
- I couldn’t come up with a better title for this post. (Naming is hard.)
Update: defining your enum
As pointed out by @peterhoneyman and @andrewa2, to avoid assuming details about the data type underlying the enum (and therefore to stay within the realm of documented behavior) you have to specify that your enum should be backed by an NSUInteger
. See this Gist from Andrew for an example.
You’ll notice that Apple does this in their header files wherever an enum is defined. Look up, for example, the definition of UITableViewCellStyle
, and you’ll notice that it’s backed by an NSInteger
. (You can also look at the definition of NS_ENUM
in NSObjCRuntime.h
to see what they’re doing.)