如何更改 iOS9 中的 UIAlertController 按钮文本颜色?

How to change UIAlertController button text colour in iOS9?

问题类似于iOS 8 UIActivityViewController and UIAlertController button text color uses window's tintColor,但在iOS 9.

我有一个 UIAlertController,即使我尝试设置,关闭按钮仍保持白色

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

UIAlertController *strongController = [UIAlertController alertControllerWithTitle:title
                                                             message:message
                                                      preferredStyle:preferredStyle];
strongController.view.tintColor = [UIColor black];

我在过去 运行 遇到过类似的事情,问题似乎源于警报控制器的视图在显示之前尚未准备好接受 tintColor 更改。或者,尝试设置色调颜色 AFTER 你展示你的警报控制器:

[self presentViewController:strongController animated:YES completion:nil];
strongController.view.tintColor = [UIColor black];

我找到了解决方法。不是一个优雅的解决方案,而是一个解决方案。

我在 UIAlertController 上调换了 viewWillAppear:,然后遍历视图并修改了色调。在我的例子中,我在整个 window 上设置了 tintColor,尽管通过外观设置了 tintColor,但 UIAlertController 保持了 window 上的颜色。我检查颜色是否与 window 的颜色相同,如果是,则应用一个新颜色。盲目地将 tintColor 应用于所有视图将导致破坏性操作的红色色调被重置。

+ (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];  
    }  
}  

但是这不适用于 iOS 8,因此您仍然需要设置外观色调。

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

我可以通过继承 UIAlertController 来解决这个问题:

class MyUIAlertController: UIAlertController {

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        //set this to whatever color you like...
        self.view.tintColor = UIColor.blackColor()
    }
}

这在显示警报时设备旋转后仍然存在。

使用此子类时,您也不需要在显示警报后设置 tintColor。

虽然在 iOS 8.4 上没有必要,但此代码也适用于 iOS 8.4。

Objective-C 实现应该是这样的:

@interface MyUIAlertController : UIAlertController
@end

@implementation MyUIAlertController
-(void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];

    //set this to whatever color you like...
    self.view.tintColor = [UIColor blackColor];
}
@end

呈现后在视图上设置色调颜色时出现问题;即使您在 presentViewController:animated:completion: 的完成块中执行此操作,它也会对按钮标题的颜色造成闪烁效果。这是草率的,不专业的,完全不能接受的。

解决这个问题并在任何地方都这样做的 sure-fire 方法是通过向 UIAlertController 添加一个类别并调整 viewWillAppear。

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

实施:

//
//  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

将 .pch(预编译 header)添加到您的项目并包含类别:

#import "UIAlertController+iOS9TintFix.h"

确保您在项目中正确注册了您的 pch,它将在每个使用 UIAlertController 的 class 中包含类别方法。

然后,在您的应用委托 didFinishLaunchingWithOptions 方法中,导入您的类别并调用

[UIAlertController tintFix];

并且它会自动传播到您应用中的每个 UIAlertController 实例,无论是由您的代码还是其他任何人启动的。

此解决方案适用于 iOS 8.X 和 iOS 9.X,并且缺少色调变化 post-presentation 方法的闪烁。

疯狂支持布兰登开始这段旅程,不幸的是我的声誉不足以评论他的post,否则我会把它留在那里!

[[UIView appearance] setTintColor:[UIColor black]];  

这将改变所有 UIView tintColor 以及 UIAlertController 的观点

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)

在Swift 2.2中你可以使用下面的代码

 // 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)

您可以使用以下方式更改它:Swift 3.x

    strongController.view.tintColor = UIColor.green

在Swift 3.x:

我发现以下方法很有效。我在应用程序启动时调用它。

UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = UIColor.black

所以这会全局更改您应用中所有 UIAlertViewController 按钮标签的色调。唯一不会改变的按钮标签颜色是具有破坏性 UIAlertActionStyle 的按钮标签颜色。

您有 3 种操作按钮样式:

let style : UIAlertActionStyle = .default
// default, cancel (bold) or destructive (red)

let alertCtrl = UIAlertController(....)
alertCtrl.addAction( UIAlertAction(title: "click me", style: style, handler: {
    _ in doWhatever()
}))

我想让删除按钮显示为红色,所以我使用了.destructive 样式:

 alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler:{(UIAlertAction) in

swift3

尝试使用 UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = MyColor 但这会阻止与 UIAlertController 无关的其他项目进行 tintColor 配置。我在尝试更改导航栏按钮项的颜色时看到它。

我切换到扩展程序(基于上面 Mike Taverne 的回复)并且效果很好。

extension UIAlertController {

override open func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    //set this to whatever color you like...
    self.view.tintColor = MyColor
}
}

最合理的方法是设置主要window的tintColor。作为一个统一的外观是我们平时所需要的。

// in app delegate
window.tintColor = ...

其他方案有缺陷

  • 使用外观

    UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = ...
    

    不适用于 iOS 9,使用 iOS 11 SDK 进行测试。

    [[UIView appearance] setTintColor:[UIColor black]];  
    

    你是认真的吗?

  • 设置 UIAlertController 视图的 tintColor 不稳定。当用户按下按钮或视图布局后颜色可能会改变。

  • 子类 UIAlertController 和覆盖布局方法是 hack 方式,这是不可接受的。

经过大量研究,我发现了如何使这项工作有效:

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)

只需将 UIColor.systemBlue 更改为您想要的任何颜色,它就会使该按钮成为特殊颜色。我制作了这个示例(我创建了 3 个 UIAlertActions 来制作它。):

只用UIAlertAction.Style.whatever,只能做成蓝色或红色。如果你改变 UIColor,它会变成你想要的任何颜色!