One of the iPhone apps I’m currently involved into requires custom buttons. Not only has our customer asked for a special look & feel, she also wants the buttons to animate smoothly in and out. We’ve decided to implement a custom button class deriving from UIButton as opposed to trying to modify a standard UIButton.
The iPhone devs among my readers know that the SDKs UIButton class is notoriously simple. In fact the only real way to style a button the way you want is to make use of background images for the various UIControlStates (normal, highlighted, etc.).
Here is were we ran into a subtle issue:
Even though UIButton has a property named backgroundImage once you assign a UIImage it does not stay in the background!
If you’re using UIButton‘s title property there’s no problem. Things start to get messy if for whatever reason you don’t want to place text on the button via the standard title property. Why would you not use the title property? Well, in our case we have to use custom fonts. Along with the custom button class we’ve created a custom label class that uses a TTF font embedded in the application bundle. We planned to add the custom label as a subview to UIButton. But as said that does not work because the background image stays in front of the label!
In fact it stays in front of every subview and obscures them all. Even if you try to bring your subviews to front via bringSubviewToFront, they never make it in front of the button’s background image.
Here’s a basic code example (typed from the top of my head, don’t expect it to compile):
UIButton *myButton = [[UIButton alloc] initWithFrame:CGRectMake(10,10,200,40)]; UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(5,5,100,20)]; myLabel.text = @"Custom Text"; myButton.backgroundImage = [UIImage imageNamed:@"anyButtonBackground.png"]; [myButton addSubview:myLabel]; [myButton bringSubviewToFront:myLabel]; [self.view addSubview:myButton];
Our expectation was that the label will sit neatly on top of the background image. It does not.
We’ve worked around this by adding the label to the button’s superview from within the custom button class. It automatically calculates the required x/y offset necessary to align superview coordinates with the button’s coordinates.
At the end of the day, we ask ourselves: Why does Apple call it a backgroundImage if it is so eager to be the topmost visible element?
And: What do you think? Is it a by design thing or should we file a bug report? I’d love to get your comments!

Yes, that is broken. I’d say file a bug report.
I found the same thing when setting the background for a tablecontrollers navbar.
Essentially, whenever I added a subview to the navigationBar, the ‘Back’ button wouldn’t respond to touches.
I wound up adding a UIImageView to the navigationBar instead of just a UIView. That solved the problem of the image being the top layer.
I ran in to this problem…
I went to the Tech Talk in Toronto, and asked an engineer about my button problems (forgot his name). He basically told me that was expected behavior, and I should go about it another way. He kind of just glazed over it, but that made me rethink my interface.
My suggestions are (none of which you will be happy about):
1. If you only have a few buttons on the screen, don’t make your custom labels a subview of the button, make them a subview of the buttons superview.
2. If you have a lot of buttons (I have over a hundred, a periodic table in case that seems crazy) and are worried about killing the processor with doubling the amount of views on screen, don’t use UIButton, use UIView or UIImageView. You can “fake” button like behavior by overriding the toucheBegan, touchesEnded, touchesMoved methods. A little bit of a chore but gives you the flexibility you want.
Good luck.
@ Corey : This is broken. A “background” view should be in the background. If this is expected behavior, Apple is more moronic than I expected.
Overlaying views ABOVE the button is all fine and dandy, until you realize that you need to manually calculate their positions when layourSubviews gets called after a screen rotation. Add to that the fact that you need to fake the button’s state, and you are reinventing the wheel.
Saying that this is expected behavior doesn’t make any sense.
Oh, I forgot the (dirty dirty hack like) solution
Just add “[self bringSubviewToFront:myView];” in a subclass of the button’s “layoutSubviews”. This require a hard reference to myView, but thats easier than laying out subviews by hand from the controller.
hey.. Im having a nightmare with Images in buttons myself, UI Classes should be the easy stuff to deal with in a project!
I wonder if you would explain how you are using a TTF in your app? I didn’t think this was possible…