Customizing UIActionSheet Buttons

One of the great benefits of iOS development is that Apple has provided the ability to customize almost any element thus not limiting a designer or developer with a stock look and feel.  Just take a look at subtle examples in Twitter for iPhone to the extreme of the Outside Weather app or Starbucks.  I am working on a nice size project and one of the requirements is to have the UIActionSheet buttons be a different color than the default gray color.  This turned out the be as straightforward as trying to change the look and feel of a tabbar.

::NOTE TO APPLE:: Customizing Tabbars and UIActionSheet buttons should be a lot easier. ::END NOTE::

It took a few hours, but I finally knocked it out.  At first glance I figured that each one of the buttons would be an instance of UIButton and I could located them in the view hierarchy and just update the background color either in a delegate method or subclass UIActionSheet and override drawRect:.  NEGATIVE.  

My next possible approach was to create my own custom view(s) and slap them on the UIActionSheet's main view and send everything else to the back.  Granted that has worked for some people and is a totally viable option, but I found it to be a very hacky solution for what I was doing.

After looking up a few questions on StackOverflow it turns out that sometime after iOS 3.1 the subviews in the UIActionSheet are an undocumented class instances of UIThreePartButton.

::BIG DISCLAIMER - YOUR APP WILL PROBABLY GET REJECTED IF YOU SUBMIT WITH THIS TECHNIQUE::

After digging up that little nugget about UIThreePartButton I finally had turned a corner.  The next hurdle was trying to find the correct method to use from the UIThreePartButton class to change the background color.  Unfortunately, there isn't a backgroundColor property for that.  Granted since I am dealing with a class that inherits form UIView I could set the subviews backgroundColor property to [UIColor redColor], but when I did that I didn't get the expected result of a button with a red background.  The button became a red tint and the view behind the button was red as well.  Obstacle.  Looking at the methods/properties that I DID have I went on a little intuition that Apple wasn't using a color, but a stretchable background image for the button.  I grabbed the background graphic that I wanted and used - (void) setBackgroundImage:.  SUCCESS!!!!!!!

I wasn't quite finished.  Background button was what I wanted, but I needed to change the font color from black to white.  But wait there isn't a method to set the font. However, UIPushButton inherits from UIThreePartButton so once again going on an educated guess/intution I was able to use setTitleColor:forState: and perfection.  Excatly what I wanted and needed.  UIActionSheet with red buttons and white font.

Even though I can't submit any app to the AppStore because of the undocumented class working through the exercise was very enlightening in the areas of ui customization, design patterns and general problem solving skills which should always be in the top of a developer's skillset.

I posted my example on github.  It was compiled with 4.0 with XCode 4 Preview 3, but XCode 3.x should work.  If you run into any problems then let me know.
16 responses
Hi, tried to build the project, it appears you didn't commit red.png
@navac100 sorry about that. I will get that image and commit it today.
@navac100 I added the red.png to the project. Let me know if anything else is missing.
This caused my app to get Rejected by Apple today ...
Did you apply the same code that I used? In the post I specifically state that it would get rejected. Apple has a zero tolerance for private API usage.
My Application was rejected too exactly for the same reason. They wrote that I'm using private APIs setBackgroundImage and setPressedImage.
Didnt he explicitly it will cause you app to get rejected.
Yes, I just confirmed it.
Try this :)
Setting the view's alpha to zero and create a new UIButton.
I think Apple does not reject this usage :)

- (void)willPresentActionSheet:(UIActionSheet *)actionSheet {
int i=0;
for (UIView* view in [actionSheet subviews]) {

if ([view isKindOfClass:NSClassFromString(@"UIAlertButton")]) {

view.alpha =0;
UIButton* bview = [[UIButton alloc ] initWithFrame:CGRectMake(5, 10 + i* 40, 310, 35)];
if (i==0) {
[bview setBackgroundImage:[UIImage imageNamed:@"BtnShowquantity.png"] forState:0];
}
else if (i==1) {
[bview setBackgroundImage:[UIImage imageNamed:@"BtnScheduleReport.png"] forState:0];
}
else if (i==2) {
[bview setBackgroundImage:[UIImage imageNamed:@"BtnShoppingReport.png"] forState:0];
}
[actionSheet addSubview:bview];
i+=1;
}
}
}

Seyma,
I created a new branch called "experimental".  Since I don't have the images to test out your below code in full either fork the repo and submit a pull request or send me the images you reference and I'll push those changes to the branch. This is an interesting approach.
-Cory

Hi there! For me, the action sheet button no longer match the UIThreePartButton class. However, the Seyma's solution seems to work. Thank you!
Hi again,
For supporting both ios 5.0 and 4.0 you have to use it like this;

[view isKindOfClass:NSClassFromString(@"UIAlertButton") || [view isKindOfClass:NSClassFromString(@"UIThreePartButton")]

If your code works then fork the repo and submit a pull request. I'll be more than happy to add it in.
Hi Cory,
Sorry for late reply.

The whole code is already in my first post, and in willPresentActionSheet function.
You can check the app "CheckMate" on AppStore if you want to see images i put on actionSheet.
But i can not post the images or the whole project code, because it is written for my company.

Take care,
Regards.

I didn't see it in the AppStore. Do you have a direct link?