41

I had been using following code to change text color of items which I add in UIActionSheet.:

- (void)willPresentActionSheet:(UIActionSheet *)actionSheet {
    for (UIView *subview in actionSheet.subviews) {
        if ([subview isKindOfClass:[UIButton class]]) {
            UIButton *button = (UIButton *)subview;
            [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        }
    }
}

It works fine in iOS7, but in iOS8 the above code doesn't change the text color of items.

Now my question is how to change the text color of items which I add in UIActionSheet using iOS8??

Additional Info:
I added breakpoint to see whether following lines from the above code get executed or not:

UIButton *button = (UIButton *)subview;
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];

These lines do not get executed. So, if([subview isKindOfClass:[UIButton class]]) is always false for all items of actionSheet.subviews. So what I think is that the view heirarchy of UIActionSheet has been changed.

Salman Zaidi
  • 8,426
  • 12
  • 42
  • 60

9 Answers9

38

There's an easy way if you still want to use UIActionSheet instead of UIAlertController in order to support older iOS versions.

UIActionSheet actually uses UIAlertController in iOS 8, and it has a private property _alertController.

SEL selector = NSSelectorFromString(@"_alertController");
if ([actionSheet respondsToSelector:selector])
{
    UIAlertController *alertController = [actionSheet valueForKey:@"_alertController"];
    if ([alertController isKindOfClass:[UIAlertController class]])
    {
        alertController.view.tintColor = [UIColor blueColor];
    }
}
else
{
    // use other methods for iOS 7 or older.
}

For Swift Below code should works

let alertAction = UIAlertAction(title: "XXX", style: .default) { (action) in

     }

    alertAction.setValue(UIColor.red, forKey: "titleTextColor")
Kuntal Gajjar
  • 706
  • 5
  • 12
nonamelive
  • 6,410
  • 8
  • 37
  • 47
  • 9
    Using private properties will probably get the app rejected – André Fratelli Jan 25 '15 at 17:50
  • 1
    To do this correct you should use UIActionSheet and UIAlertView on iOS7 and below and UIAlertController on iOS8 and above. For my projects I wrote a 'custom' AlertController that automatically picks the right method based on the OS version. To bad I cannot share but I would definitely recommend doing this also because there are some bugs that might occur on iOS8 when using UIActionSheet – Saren Inden Jan 29 '15 at 08:32
  • Will this be rejected by apple? If I use this? – Syed Ali Salman Apr 04 '16 at 08:45
  • Great, but how do you know that pro ? – Tung Vu Duc Jul 12 '18 at 02:13
33

To change the button title color you need to use UIAlertController and set the tintColor of the main view.

Example of setting the button title colors to black:

UIAlertController *actionSheetController = [UIAlertController alertControllerWithTitle:@"How would you like to proceed?" message:@"" preferredStyle:UIAlertControllerStyleActionSheet];

UIAlertAction *finish = [UIAlertAction actionWithTitle:@"Finish" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action)
                               {
                                   [self performSelector:@selector(finish:) withObject:nil];
                               }];

UIAlertAction *playAgain = [UIAlertAction actionWithTitle:@"Play Again" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action)
                            {
                                [self performSelector:@selector(playAgain:) withObject:nil];
                            }];

UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action)
                         {
                             [actionSheetController dismissViewControllerAnimated:YES completion:nil];
                         }];

[actionSheetController addAction:finish];
[actionSheetController addAction:playAgain];
[actionSheetController addAction:cancel];

//******** THIS IS THE IMPORTANT PART!!!  ***********
actionSheetController.view.tintColor = [UIColor blackColor];

[self presentViewController:actionSheetController animated:YES completion:nil];
DiscDev
  • 36,864
  • 20
  • 113
  • 130
  • 5
    This is a great answer. Although, I'm still looking for a way to set each button tint as well as the backgroundColor of the `UIAlertController`. Any thoughts? – Jorge Sep 18 '14 at 18:09
  • 1
    @Jorge I remember reading somewhere that Apple doesn't want you to customize action sheet / alert colors. I believe that if you want to do more customizations than what I mentioned you'd need to make your own alert class, or borrow one of the many readily available open source options from github. – DiscDev Sep 18 '14 at 18:11
  • Yes, that's what I thought. Actually, I do use `UIView` with buttons for this purpose sometimes, but it would be a lot easier to just customize `UIAlertController`. Thanks a lot. @DiscDev – Jorge Sep 18 '14 at 18:15
  • 1
    This is not working with style `.Alert`. Most likely Apple does not foresee the modification of alerts. – Mundi Sep 20 '14 at 10:02
  • 1
    Can we at least change the font and title color? Apple should atleast allowed us to do simple customization rather than using uiview that would function like an alertview or actionsheet. – Teffi Sep 23 '14 at 06:34
  • 2
    Yes, part of the purpose of GUI (graphical) is to make it clear and obvious sometimes by color, such as when one is about to Delete something, the color red is used to indicate, you are about to do something with potential consequences. Some of apples choices since Steve Job's passing do not seem wise. – Tim Sep 29 '14 at 03:16
  • @Tim the iOS SDK has never allowed you to customize action sheets or alerts colors from version 1. Doesn't have anything to do with the late Mr. Jobs. They want a consistent experience. If you want something more flexible, you'll have to make it yourself. – DiscDev Sep 29 '14 at 15:11
  • DiscDev, thanks, is it possible to change the individual UIAlertAction button different color? – Whoami Oct 08 '14 at 08:31
  • @Whoami doubtful. Apple doesn't want you to customize alerts/actionsheets. They suggest you make your own if you want a custom look and feel. There are lots of projects on github for this, go have a look there. – DiscDev Oct 08 '14 at 16:22
  • Setting the tintColor works well, but the gap between Default- and Cancel-Style-Items is than missing :( – matzino Feb 11 '15 at 15:17
  • I had to set actionSheetController.view.tintColor = [UIColor blackColor]; again in the completion block to have not flicker the color after tapping on the buttons! – Pauls Aug 25 '16 at 11:47
10

Ok, I ran into the same problem but I think I have found a solution:

The appropriate way should be like this and I guess it works in iOS7:

[[UIButton appearanceWhenContainedIn:[UIActionSheet class], nil] setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];

But it will not work in iOS8 due to the fact that the ActionSheets "buttons" is now based on a UICollectionView. So after some digging around I got this to work instead:

[[UICollectionView appearanceWhenContainedIn:[UIAlertController class], nil] setTintColor:[UIColor blueColor]];
Janne
  • 364
  • 5
  • 10
  • 3
    Am I the only sees that this changes color for all buttons except for Cancel button? – huong Oct 09 '14 at 08:25
  • This will definitely have unintended side effects and is not advised. Changing the tint color for all UICollectionViews inside UIAlertController is fragile and relies on the view hierarchy Apple has chosen for the current version of iOS. As you can see from this answer, they changed the view hierarchy from iOS 7-> iOS 8, what's to keep them from doing it again in iOS 9? This will surely require updating in the future and is not advised. – DiscDev Oct 28 '14 at 13:56
  • @friedegg-bacon-sandwich i'm getting everything in tint color except the cancel button as well. looks extremely stupid. do you know how to set the cancel button to tint color as well? – mnl Oct 09 '15 at 16:20
  • @friedegg-bacon-sandwich Did anybody ever solve the stubborn cancel button issue? – BFeher Dec 17 '15 at 03:00
9

I'm using it.

[[UIView appearanceWhenContainedIn:[UIAlertController class], nil] setTintColor:[UIColor blueColor]];

Add one line (AppDelegate) and works for all UIAlertController.

Lucas Torquato
  • 407
  • 4
  • 8
  • 1
    Best solution imo. I added [a few tweaks](http://stackoverflow.com/a/29262883/2547229) for backwards compatibility and to get the default tint colour, which isn't `UIColor.blueColor`. – Benjohn Mar 25 '15 at 17:49
5

I have same task and I've made this hack today, dirty, but it works

class CustomAlertViewController: UIAlertController {

    internal var cancelText: String?


    private var font: UIFont? = UIFont(name: "MuseoSansCyrl-500", size: 12)

    override func viewDidLoad() {
        super.viewDidLoad()

        self.view.tintColor = UIColor.blackColor()
    }

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        self.findLabel(self.view) //if you need ios 9 only, this can be made in viewWillAppear
    }

    func findLabel(scanView: UIView!) {
        if (scanView.subviews.count > 0) {
            for subview in scanView.subviews {
                if let label: UILabel = subview as? UILabel {

                    if (self.cancelText != nil && label.text == self.cancelText!) {
                        dispatch_async(dispatch_get_main_queue(),{
                            label.textColor = UIColor.redColor() //for ios 8.x
                            label.tintColor = UIColor.redColor() //for ios 9.x
                        })
                    }

                    if (self.font != nil) {
                        label.font = self.font
                    }
                }

                self.findLabel(subview)
            }
        }
    }

}
Igor
  • 1,517
  • 10
  • 12
3

Changing the tintColor of window worked for me.

In the - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method

write this:

[self.window setTintColor:[UIColor redColor]];

This changed the font color of all UIActionSheet objects.

Abdullah Umer
  • 3,788
  • 5
  • 31
  • 56
3

For iOS 7 backwards compatibility, and to collect the proper default tint colour (that Apple may well change in future versions), I use this. Thanks to the answers about default tint and to Lucas's answer above.

    const Class alertControllerClass = NSClassFromString(@"UIAlertController");
    if(alertControllerClass)
    {
        UIColor *defaultTintColour = UIView.new.tintColor;
        [[UIView appearanceWhenContainedIn: alertControllerClass, nil] setTintColor: defaultTintColour];
    }

Caution though – you need to ensure that you use this before you start setting the global tint otherwise the UIView.new.tintColor will just give you the current tint you have set up.

Community
  • 1
  • 1
Benjohn
  • 12,147
  • 8
  • 58
  • 110
  • 1
    This doesn't work for iPads on iOS 9, for some reason. Poo. :-( – Benjohn Oct 02 '15 at 12:42
  • [self.window setTintColor:[UIColor whateverColor]] is the only thing I've found that works in iOS 8 and 9 – whyoz Oct 05 '15 at 17:17
  • @whyoz I'll look at that – I've already set this to something else for some reason, but perhaps I can achieve that elsewhere… – Benjohn Oct 06 '15 at 15:38
3

Swift 4

    let alert =  UIAlertController(title: "title", message: "message", preferredStyle: .alert)
    alert.view.tintColor = UIColor.black
    self.present(alert, animated: true, completion: nil)
Lukas Mohs
  • 180
  • 1
  • 4
2

For Swift you can do like this

    let alertAction = UIAlertAction(title: "XXX", style: .default) { (action) in

     }

    alertAction.setValue(UIColor.red, forKey: "titleTextColor")
Kuntal Gajjar
  • 706
  • 5
  • 12