Reputation: 1468
The question is similar to iOS 8 UIActivityViewController and UIAlertController button text color uses window's tintColor but in iOS 9.
I have a UIAlertController and the dismiss button keeps white colour even I have tried to set
[[UIView appearanceWhenContainedIn:[UIAlertController class], nil] setTintColor:[UIColor blackColor]];
UIAlertController *strongController = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:preferredStyle];
strongController.view.tintColor = [UIColor black];
Upvotes: 38
Views: 40714
Reputation:
After alot of research, I found out how to make this work:
let cancelButton = UIAlertAction(title: button, style: UIAlertAction.Style.cancel, handler: { (action) in alert.dismiss(animated: true, completion: nil)
})
cancelButton.setValue(UIColor.systemBlue, forKey: "titleTextColor")
alert.addAction(cancelButton)
Just change the UIColor.systemBlue to what ever color you want, and it will make just that button a special color. I made this example (I created 3 UIAlertActions to make it.):
With just UIAlertAction.Style.whatever, it can only make it blue or red. If you change the UIColor, it will make it any color you want!
Upvotes: 17
Reputation: 2720
The most reasonable way is set the main window's tintColor. As a uniform appearance is what we usually need.
// in app delegate
window.tintColor = ...
Other solutions have defects
Use apperance
UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = ...
Not works on iOS 9, tests with iOS 11 SDK.
[[UIView appearance] setTintColor:[UIColor black]];
Are you serious?
Set UIAlertController view's tintColor is unstable. The color may change when user press the button or after view layout.
Subclass UIAlertController and overwrite layout method is hack way which is unacceptable.
Upvotes: 3
Reputation: 4827
Objective-C
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Title text" message:@"Message text" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction actionWithTitle:@"Yes" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
//code here…
}];
UIAlertAction* cancel = [UIAlertAction actionWithTitle:@"Later" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
//code here….
}];
[ok setValue:[UIColor greenColor] forKey:@"titleTextColor"];
[cancel setValue:[UIColor redColor] forKey:@"titleTextColor"];
[alertController addAction:ok];
[alertController addAction:cancel];
[alertController.view setTintColor:[UIColor yellowColor]];
[self presentViewController:alertController animated:YES completion:nil];
Swift 3
let alertController = UIAlertController(title: "Title text", message: "Message text", preferredStyle: .alert)
let ok = UIAlertAction(title: "Yes" , style: .default) { (_ action) in
//code here…
}
let cancel = UIAlertAction(title: "Later" , style: .default) { (_ action) in
//code here…
}
ok.setValue(UIColor.green, forKey: "titleTextColor")
cancel.setValue(UIColor.red, forKey: "titleTextColor")
alertController.addAction(ok)
alertController.addAction(cancel)
alertController.view.tintColor = .yellow
self.present(alertController, animated: true, completion: nil)
Upvotes: 21
Reputation: 4436
swift3
Tried to use UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = MyColor
but this prevents other items unrelated to the UIAlertController
from tintColor configuration. I saw it while trying to change the color of navigation bar button items.
I switched to an extension (based on Mike Taverne's response above) and it works great.
extension UIAlertController {
override open func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
//set this to whatever color you like...
self.view.tintColor = MyColor
}
}
Upvotes: 6
Reputation: 8671
I wanted to make the delete button to appear red, so I used .destructive style:
alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler:{(UIAlertAction) in
Upvotes: 1
Reputation: 11519
You have 3 styles for the action buttons:
let style : UIAlertActionStyle = .default
// default, cancel (bold) or destructive (red)
let alertCtrl = UIAlertController(....)
alertCtrl.addAction( UIAlertAction(title: "click me", style: style, handler: {
_ in doWhatever()
}))
Upvotes: 0
Reputation: 1051
In Swift 3.x:
I found the following to work effectively. I call this at app launch .
UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = UIColor.black
So this would change the tint color of all UIAlertViewController button labels in your app globally. The only button label color it doesn't change are those which have a UIAlertActionStyle of destructive.
Upvotes: 26
Reputation: 4451
You can change it using: Swift 3.x
strongController.view.tintColor = UIColor.green
Upvotes: 5
Reputation: 1213
In Swift 2.2 you can use following code
// LogOut or Cancel
let logOutActionSheet: UIAlertController = UIAlertController(title: "Hello Mohsin!", message: "Are you sure you want to logout?", preferredStyle: .Alert)
self.presentViewController(logOutActionSheet, animated: true, completion: nil)
let cancelActionButton: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) { action -> Void in
print("Cancel Tapped")
}
logOutActionSheet.addAction(cancelActionButton)
let logOutActionButton: UIAlertAction = UIAlertAction(title: "Clear All", style: .Default)
{ action -> Void in
//Clear All Method
print("Logout Tapped")
}
logOutActionButton.setValue(UIColor.redColor(), forKey: "titleTextColor")
logOutActionSheet.addAction(logOutActionButton)
Upvotes: 1
Reputation: 121
[[UIView appearance] setTintColor:[UIColor black]];
this will change all the UIView tintColor
as well as UIAlertController
's view
Upvotes: 4
Reputation: 131
There is a problem with setting the tint color on the view after presenting; even if you do it in the completion block of presentViewController:animated:completion:, it causes a flicker effect on the color of the button titles. This is sloppy, unprofessional and completely unacceptable.
The one sure-fire way to solve this problem and to do it everywhere, is via adding a category to UIAlertController and swizzling the viewWillAppear.
The header:
//
// UIAlertController+iOS9TintFix.h
//
// Created by Flor, Daniel J on 11/2/15.
//
#import <UIKit/UIKit.h>
@interface UIAlertController (iOS9TintFix)
+ (void)tintFix;
- (void)swizzledViewWillAppear:(BOOL)animated;
@end
The implementation:
//
// UIAlertController+iOS9TintFix.m
//
// Created by Flor, Daniel J on 11/2/15.
//
#import "UIAlertController+iOS9TintFix.h"
#import <objc/runtime.h>
@implementation UIAlertController (iOS9TintFix)
+ (void)tintFix {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method method = class_getInstanceMethod(self, @selector(viewWillAppear:));
Method swizzle = class_getInstanceMethod(self, @selector(swizzledViewWillAppear:));
method_exchangeImplementations(method, swizzle);});
}
- (void)swizzledViewWillAppear:(BOOL)animated {
[self swizzledViewWillAppear:animated];
for (UIView *view in self.view.subviews) {
if (view.tintColor == self.view.tintColor) {
//only do those that match the main view, so we don't strip the red-tint from destructive buttons.
self.view.tintColor = [UIColor colorWithRed:0.0 green:122.0/255.0 blue:1.0 alpha:1.0];
[view setNeedsDisplay];
}
}
}
@end
Add a .pch (precompiled header) to your project and include the category:
#import "UIAlertController+iOS9TintFix.h"
Make sure you register your pch in the project properly, and it will include the category methods in every class that uses the UIAlertController.
Then, in your app delegates didFinishLaunchingWithOptions method, import your category and call
[UIAlertController tintFix];
and it will automatically propagate to every single instance of UIAlertController within your app, whether launched by your code or anyone else's.
This solution works for both iOS 8.X and iOS 9.X and lacks the flicker of the tint change post-presentation approach.
Mad props to Brandon above for starting this journey, unfortunately my reputation was not sufficient enough to comment on his post, or else I would have left it there!
Upvotes: 4
Reputation: 9352
I was able to solve this by subclassing UIAlertController
:
class MyUIAlertController: UIAlertController {
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
//set this to whatever color you like...
self.view.tintColor = UIColor.blackColor()
}
}
This survives a device rotation while the alert is showing.
You also don't need to set the tintColor after presenting the alert when using this subclass.
Though it isn't necessary on iOS 8.4, this code does work on iOS 8.4 as well.
Objective-C implementation should be something like this:
@interface MyUIAlertController : UIAlertController
@end
@implementation MyUIAlertController
-(void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
//set this to whatever color you like...
self.view.tintColor = [UIColor blackColor];
}
@end
Upvotes: 14
Reputation: 5088
I found a solution to this. Not an elegant solution, but a solution.
I swizzled viewWillAppear: on UIAlertController, then looped through the views and modified the tint color. In my case I had a tintColor set on the entire window and despite setting the tintColor via appearance the UIAlertController maintained the color on the window. I check if the color is equal to that of the window and if so apply a new one. Blindly applying the tintColor to all views will result in the red tint on destructive actions to be reset.
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method swizzleMethod = class_getInstanceMethod(self, @selector(viewWillAppear:));
Method method = class_getInstanceMethod(self, @selector(alertSwizzle_viewWillAppear:));
method_exchangeImplementations(method, swizzleMethod);
});
}
- (void)alertSwizzle_viewWillAppear:(BOOL)animated
{
[self alertSwizzle_viewWillAppear:animated];
[self applyTintToView:self.view];
}
- (void)applyTintToView:(UIView *)view
{
UIWindow *mainWindow = [UIApplication sharedApplication].keyWindow;
for (UIView *v in view.subviews) {
if ([v.tintColor isEqual:mainWindow.tintColor]) {
v.tintColor = [UIColor greenColor];
}
[self applyTintToView:v];
}
}
However this doesn't work on iOS 8, so you'll still need to set the apperance tint color.
[[UIView appearanceWhenContainedIn:[UIAlertController class], nil] setTintColor:[UIColor greenColor]];
Upvotes: 2
Reputation: 5566
I've run into something similar in the past and the issue seems to stem from the fact that the alert controller's view isn't ready to accept tintColor
changes before it's presented. Alternatively, try setting the tint color AFTER you present your alert controller:
[self presentViewController:strongController animated:YES completion:nil];
strongController.view.tintColor = [UIColor black];
Upvotes: 62