Mixing Tab Bars and Action Sheets
iPhone learning continues apace. Today I discovered a surprising interaction between tab bars and action sheets. This is worth remembering for my own sake, but I think it’s also worth passing on for three reasons. First, if you happen to have just this problem, I can save you some time. Second, it illustrates how looking at something can lead us to jump to wrong conclusions. Finally, it points out that in our wonderful new technological world, we have to deal regularly with a nightmare of crazy detail.
My app has a tab bar along the bottom. That’s a row of rectangular buttons that you tap to move from one screen to another in the program. Lots of iPhone apps have them; they’re a standard built-in element in Apple’s UI library. On one of my pages, I wanted to bring up an action sheet. That’s a little sub-screen full of buttons that slides up from the bottom of the phone’s display. It’s meant as a temporary little window to let your user choose among a few options. Once you click one of the buttons, it goes away again. The action sheet is also a standard UI element.
In the screenshot below, the leftmost picture shows my little test project. There’s a tab bar at the bottom with just two tabs (”First” and “Second” - this is in fact just the default setup that Apple makes for you when you start a “tab bar project”). The window has two buttons - the top one causes an action sheet to appear. The middle screenshot shows the result.

To me, this looks pretty fine. There’s just one problem: half of the Cancel button doesn’t work. If you happen to tap on the upper two buttons, or the top half of the Cancel button, they work as expected. But if you tap on the bottom half of the Cancel button, your tap is ignored. Forgotten. Nothing happens.
I finally figured out the heart of the problem: the Cancel button is straddling the tab bar. The lower half of the button overlaps the tab bar, and that’s the part that’s not responding to taps. But that just didn’t make sense to me. Clearly the purpose of an action sheet is to sit in front of all open windows (as this one does) and accept the user’s input. I was guessing that my touches were going to the tab bar, but why? The action sheet clearly sits in front of everything.
And that’s how looking at the picture led to the wrong conclusion. The picture shows the action sheet in front of the tab bar, fully visible. But that’s not what’s happening internally. The action sheet is brought up by the UIViewController that is responsible for the big white rectangular region on the leftmost screenshot (everything below the status bar and above the tab bar). When the action sheet gets created, you have to call a method called showInView to tell it which view to be drawn in. Naturally enough, I gave it the view controlled by that view controller. Remember, that view starts below the status bar and stops at the top of the tab bar. So of course the action sheet wasn’t getting my touches; they’re outside of the view it belongs to.
But it seems to me that my mistake was reasonable. If the action sheet really does belong to that view, then all we should see of it is the part that is within that view. The Photoshopped screenshot on the far right shows what I assert should have been displayed. Then the problem would be obvious. But for some reason, they draw the whole thing, even the part that’s outside of the view it belongs to, which seems to violate the spirit both of what views are all about and what action sheets are about. And it’s inconsistent: the graphics extend beyond the view, but the input handling does not. It seems to me that if the action sheet appears in front of everything else (which is what you want it to do), then it should accept input from anywhere inside its boundaries.
The workaround, once we know the problem, is easy: instead of creating the action sheet in the view controller’s view (the white rectangle), we create it in the tab bar controller’s view, which extends from the bottom of the status bar to the bottom of the screen. It takes a little monkey business to get the right variable into the view controller so it’s available when we create the action sheet, but that’s just programming. Unfortunately, it can be a hassle. The iPhone is deliberately designed to let you “drill down” many levels of a view hierarchy - they even have specific view controllers designed to support that. If any of those views will need to put up an action sheet, they’ll all have to somehow get access to the root controller’s view. Ick.
It took me about an hour to work all of this out. In retrospect, both the problem and the solution are easy, but what I saw on the screen kept telling me one story, while the code was telling me another. When I stopped believing the picture and focused just on the program, I saw the problem pretty quickly. Which brings me to the third point: there is so much random detail to master with today’s rich programming environments. Back in the days of BASIC, you learned the language and it got interpreted and that was that. There really weren’t any subtleties. Today things are so sophisticated and complex that they save us enormous amounts of time and effort when they work properly, but they can drive us crazy when things go even a little bit askew.
Leave a comment
You must be logged in to post a comment.