UIAlertController自定义字体、大小、颜色

128

我正在使用新的UIAlertController来显示警报。我有以下代码:

// nil titles break alert interface on iOS 8.0, so we'll be using empty strings
UIAlertController *alert = [UIAlertController alertControllerWithTitle: title == nil ? @"": title message: message preferredStyle: UIAlertControllerStyleAlert];


UIAlertAction *defaultAction = [UIAlertAction actionWithTitle: cancelButtonTitle style: UIAlertActionStyleCancel handler: nil];

[alert addAction: defaultAction];

UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
[rootViewController presentViewController:alert animated:YES completion:nil];

现在我想改变标题和消息的字体、颜色和大小等。有什么最好的方法来做到这一点吗?

编辑: 我应该插入整个代码。我创建了一个UIView的类别,可以在iOS版本中显示正确的警报。

@implementation UIView (AlertCompatibility)

+( void )showSimpleAlertWithTitle:( NSString * )title
                          message:( NSString * )message
                cancelButtonTitle:( NSString * )cancelButtonTitle
{
    float iOSVersion = [[UIDevice currentDevice].systemVersion floatValue];
    if (iOSVersion < 8.0f)
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle: title
                                                        message: message
                                                       delegate: nil
                                              cancelButtonTitle: cancelButtonTitle
                                              otherButtonTitles: nil];
        [alert show];
    }
    else
    {
        // nil titles break alert interface on iOS 8.0, so we'll be using empty strings
        UIAlertController *alert = [UIAlertController alertControllerWithTitle: title == nil ? @"": title
                                                                       message: message
                                                                preferredStyle: UIAlertControllerStyleAlert];


        UIAlertAction *defaultAction = [UIAlertAction actionWithTitle: cancelButtonTitle
                                                                style: UIAlertActionStyleCancel
                                                              handler: nil];

        [alert addAction: defaultAction];

        UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
        [rootViewController presentViewController:alert animated:YES completion:nil];
    }
}

2
免责声明:对于阅读以下答案的任何人。如果您倾向于使用任何私有API,苹果将拒绝您的应用程序。在下面的答案中,这就是发生的事情。 - Yash Bedi
25个回答

101

我不确定这是否违反了私有API/属性,但在iOS8上使用KVC对我很有效。

UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"Dont care what goes here, since we're about to change below" message:@"" preferredStyle:UIAlertControllerStyleActionSheet];
NSMutableAttributedString *hogan = [[NSMutableAttributedString alloc] initWithString:@"Presenting the great... Hulk Hogan!"];
[hogan addAttribute:NSFontAttributeName
              value:[UIFont systemFontOfSize:50.0]
              range:NSMakeRange(24, 11)];
[alertVC setValue:hogan forKey:@"attributedTitle"];



UIAlertAction *button = [UIAlertAction actionWithTitle:@"Label text" 
                                        style:UIAlertActionStyleDefault
                                        handler:^(UIAlertAction *action){
                                                    //add code to make something happen once tapped
}];
UIImage *accessoryImage = [UIImage imageNamed:@"someImage"];
[button setValue:accessoryImage forKey:@"image"];

顺便说一下,使用私有API也可以更改警告操作的字体。同样地,这可能会导致您的应用被拒绝,我还没有尝试过提交这样的代码。

let alert = UIAlertController(title: nil, message: nil, preferredStyle: .ActionSheet)

let action = UIAlertAction(title: "Some title", style: .Default, handler: nil)
let attributedText = NSMutableAttributedString(string: "Some title")

let range = NSRange(location: 0, length: attributedText.length)
attributedText.addAttribute(NSKernAttributeName, value: 1.5, range: range)
attributedText.addAttribute(NSFontAttributeName, value: UIFont(name: "ProximaNova-Semibold", size: 20.0)!, range: range)

alert.addAction(action)

presentViewController(alert, animated: true, completion: nil)

// this has to be set after presenting the alert, otherwise the internal property __representer is nil
guard let label = action.valueForKey("__representer")?.valueForKey("label") as? UILabel else { return }
label.attributedText = attributedText

对于Xcode 10及以上版本的Swift 4.2,最后两行现在应该是:

guard let label = (action!.value(forKey: "__representer")as? NSObject)?.value(forKey: "label") as? UILabel else { return }
        label.attributedText = attributedText

6
正常工作。attributedTitle表示标题,attributedMessage表示消息。不确定是否是最佳解决方案,但目前对我来说已足够好了。 - Libor Zapletal
2
我们可以在UIAlertController的按钮上添加哪些自定义内容? - Aanchal Chaurasia
2
谢谢!我有一个小问题——在UIAlertController中,可以使用自定义字体和颜色来设置tilemessage属性。那么如何在UIAlertAction中实现相同的效果呢? - p0lAris
84
我希望你们中没有人打算将这个应用发布到应用商店,因为它使用私有API。说真的,在Stackoverflow上,我不知道为什么这些非真正的“答案”被接受了。这些只是一些可能会被允许在应用商店发布的技巧。 - TheCodingArt
4
在将应用发布到应用商店的情况下,Apple允许使用某些私有API,但不应该使用可能会损害或影响用户系统/隐私的API。因此,仅因为这个原因,可能会有很多人接受这个答案。并且可能不会对应用商店产生影响。有没有使用过这个API的人可以确认他们的应用程序是否被拒绝? - Mehul Thakkar
显示剩余8条评论

69

您可以通过在UIAlertController上应用一个色调颜色来更改按钮的颜色。

在iOS 9上,如果窗口色调颜色被设置为自定义颜色,则必须在呈现警报后立即应用色调颜色。否则,色调颜色将重置为您的自定义窗口色调颜色。

// In your AppDelegate for example:
window?.tintColor = UIColor.redColor()

// Elsewhere in the App:
let alertVC = UIAlertController(title: "Title", message: "message", preferredStyle: .Alert)
alertVC.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil))
alertVC.addAction(UIAlertAction(title: "Ok", style: .Default, handler: nil))

// Works on iOS 8, but not on iOS 9
// On iOS 9 the button color will be red
alertVC.view.tintColor = UIColor.greenColor()

self.presentViewController(alert, animated: true, completion: nil)

// Necessary to apply tint on iOS 9
alertVC.view.tintColor = UIColor.greenColor()

22
为了澄清,展示控制器后设置tintColor在iOS 8和9上都有效,因此不需要设置两次。 - arlomedia
感谢您为iOS 9中的此问题添加了一种Swift答案和一种解决方法。 - peacetype
3
当我用手指向下轻触并拖动时,它会恢复默认颜色。 有什么想法吗? - msmq
这确实是此处唯一体面的答案。 - TheCodingArt

53

您可以使用以下代码更改按钮文本的颜色:

alertC.view.tintColor = your color;

也许这会对你有所帮助。


@esilver,你找到适用于iOS9的解决方案了吗? - Ash
1
我没有这样做。我向苹果提交了一个错误报告,编号为#22391695。 - esilver
1
关于此事还有更多信息。似乎当您在长列表中滚动时,您触摸要滚动的那个项目会变成蓝色... - Bejil
3
在iOS9和9.1上,UIAlertController无法正常工作。不知道苹果开发人员在搞什么鬼... 每次调用警告控制器时需要手动更改窗口色调,并在处理程序中将其改回。 - Akhilesh Sharma
它适用于iOS 9.3,除非你在屏幕外进行触摸:否则会回到系统蓝屏。 - Rémi Belzanti

39
在Xcode 8和Swift 3.0中。
@IBAction func touchUpInside(_ sender: UIButton) {

    let alertController = UIAlertController(title: "", message: "", preferredStyle: .alert)

    //to change font of title and message.
    let titleFont = [NSFontAttributeName: UIFont(name: "ArialHebrew-Bold", size: 18.0)!]
    let messageFont = [NSFontAttributeName: UIFont(name: "Avenir-Roman", size: 12.0)!]

    let titleAttrString = NSMutableAttributedString(string: "Title Here", attributes: titleFont)
    let messageAttrString = NSMutableAttributedString(string: "Message Here", attributes: messageFont)

    alertController.setValue(titleAttrString, forKey: "attributedTitle")
    alertController.setValue(messageAttrString, forKey: "attributedMessage")

    let action1 = UIAlertAction(title: "Action 1", style: .default) { (action) in
        print("\(action.title)")
    }

    let action2 = UIAlertAction(title: "Action 2", style: .default) { (action) in
        print("\(action.title)")
    }

    let action3 = UIAlertAction(title: "Action 3", style: .default) { (action) in
        print("\(action.title)")
    }

    let okAction = UIAlertAction(title: "Ok", style: .default) { (action) in
        print("\(action.title)")
    }

    alertController.addAction(action1)
    alertController.addAction(action2)
    alertController.addAction(action3)
    alertController.addAction(okAction)

    alertController.view.tintColor = UIColor.blue
    alertController.view.backgroundColor = UIColor.black
    alertController.view.layer.cornerRadius = 40

    present(alertController, animated: true, completion: nil)

}

输出

UIAlertController自定义字体、大小和颜色


抱歉兄弟,这是初创阶段。如果将来需要,我会通知你的... - Naresh

25

一份@dupuis2387答案的Swift翻译。通过KVC使用attributedTitle键设置UIAlertController标题的颜色和字体语法已经解决。

let message = "Some message goes here."
let alertController = UIAlertController(
    title: "", // This gets overridden below.
    message: message,
    preferredStyle: .Alert
)
let okAction = UIAlertAction(title: "OK", style: .Cancel) { _ -> Void in
}
alertController.addAction(okAction)

let fontAwesomeHeart = "\u{f004}"
let fontAwesomeFont = UIFont(name: "FontAwesome", size: 17)!
let customTitle:NSString = "I \(fontAwesomeHeart) Swift" // Use NSString, which lets you call rangeOfString()
let systemBoldAttributes:[String : AnyObject] = [ 
    // setting the attributed title wipes out the default bold font,
    // so we need to reconstruct it.
    NSFontAttributeName : UIFont.boldSystemFontOfSize(17)
]
let attributedString = NSMutableAttributedString(string: customTitle as String, attributes:systemBoldAttributes)
let fontAwesomeAttributes = [
    NSFontAttributeName: fontAwesomeFont,
    NSForegroundColorAttributeName : UIColor.redColor()
]
let matchRange = customTitle.rangeOfString(fontAwesomeHeart)
attributedString.addAttributes(fontAwesomeAttributes, range: matchRange)
alertController.setValue(attributedString, forKey: "attributedTitle")

self.presentViewController(alertController, animated: true, completion: nil)

enter image description here


3
“OK”按钮怎么样?我们能定制它吗? - Hassan Taleb
@HassanTaleb 我还没有找到一个很好的定制按钮的方法。你可以在view上设置tintColor或通过appearanceWhenContainedIn来设置,但是一旦你触摸它,色调就会消失。不过我仍在寻找答案。 - Robert Chen
@AbdulMomen عبدالمؤمن,你看到了什么错误信息?这段代码片段假定FontAwesome已经设置好了。 - Robert Chen
@RobertChen在 alertController.setValue(attributedString, forKey: "attributedTitle")中不能识别attributedTitle - AbdulMomen عبدالمؤمن
1
@RobertChen 要解决这个问题,只需在 self.presentViewController(alertController, animated: true, completion: nil) 后面加上 tint 即可。我们可以更改“确定”按钮的字体吗? - Hassan Taleb
4
这不算私有API吗? - Jinghan Wang

22

Swift 5和5.1。创建一个单独的文件,并将UIAlertController定制代码放在那里。

import Foundation
import  UIKit

extension UIAlertController {

  //Set background color of UIAlertController
  func setBackgroudColor(color: UIColor) {
    if let bgView = self.view.subviews.first,
      let groupView = bgView.subviews.first,
      let contentView = groupView.subviews.first {
      contentView.backgroundColor = color
    }
  }

  //Set title font and title color
  func setTitle(font: UIFont?, color: UIColor?) {
    guard let title = self.title else { return }
    let attributeString = NSMutableAttributedString(string: title)//1
    if let titleFont = font {
      attributeString.addAttributes([NSAttributedString.Key.font : titleFont],//2
        range: NSMakeRange(0, title.utf8.count))
    }
    if let titleColor = color {
      attributeString.addAttributes([NSAttributedString.Key.foregroundColor : titleColor],//3
        range: NSMakeRange(0, title.utf8.count))
    }
    self.setValue(attributeString, forKey: "attributedTitle")//4
  }

  //Set message font and message color
  func setMessage(font: UIFont?, color: UIColor?) {
    guard let title = self.message else {
      return
    }
    let attributedString = NSMutableAttributedString(string: title)
    if let titleFont = font {
      attributedString.addAttributes([NSAttributedString.Key.font : titleFont], range: NSMakeRange(0, title.utf8.count))
    }
    if let titleColor = color {
      attributedString.addAttributes([NSAttributedString.Key.foregroundColor : titleColor], range: NSMakeRange(0, title.utf8.count))
    }
    self.setValue(attributedString, forKey: "attributedMessage")//4
  }

  //Set tint color of UIAlertController
  func setTint(color: UIColor) {
    self.view.tintColor = color
  }
}

现在,在任何操作上显示警报

  func tapShowAlert(sender: UIButton) {
    let alertController = UIAlertController(title: "Alert!!", message: "This is custom alert message", preferredStyle: .alert)
    // Change font and color of title
    alertController.setTitle(font: UIFont.boldSystemFont(ofSize: 26), color: UIColor.yellow)
    // Change font and color of message
    alertController.setMessage(font: UIFont(name: "AvenirNextCondensed-HeavyItalic", size: 18), color: UIColor.red)
    // Change background color of UIAlertController
    alertController.setBackgroudColor(color: UIColor.black)
    let actnOk = UIAlertAction(title: "Ok", style: .default, handler: nil)
    let actnCancel = UIAlertAction(title: "Cancel", style: .default, handler: nil)
    alertController.addAction(actnOk)
    alertController.addAction(actnCancel)
    self.present(alertController, animated: true, completion: nil)
  }

结果

输入图像描述


1
这里是否正在访问任何私有API?您是否发布了具有这么多自定义警报属性的应用程序? - Yash Bedi
@YashBedi 这个使用了私有API,苹果可能会因为“非公共API”使用而拒绝你的应用。不,我还没有发布任何应用。 - Gurjinder Singh
4
重要提示:UIAlertController类旨在按原样使用,不支持子类化。该类的视图层次结构是私有的,不得修改。 - Gurjinder Singh
@Darkglow 请指出错误。我能够成功地使用Swift 5.1构建相同的代码。 - Gurjinder Singh

14

使用 UIAppearance 协议。设置字体的示例 - 创建一个分类来扩展 UILabel

@interface UILabel (FontAppearance)
@property (nonatomic, copy) UIFont * appearanceFont UI_APPEARANCE_SELECTOR;
@end


@implementation UILabel (FontAppearance)

-(void)setAppearanceFont:(UIFont *)font {
    if (font)
        [self setFont:font];
}

-(UIFont *)appearanceFont {
    return self.font;
}

@end

以及它的用法:

UILabel * appearanceLabel = [UILabel appearanceWhenContainedIn:UIAlertController.class, nil];
[appearanceLabel setAppearanceFont:[UIFont boldSystemFontOfSize:10]]; //for example

已经测试并且可以与样式UIAlertControllerStyleActionSheet一起使用,但我猜也可以与UIAlertControllerStyleAlert一起使用。

附注:最好检查类的可用性而不是iOS版本:

if ([UIAlertController class]) {
    // UIAlertController code (iOS 8)
} else {
    // UIAlertView code (pre iOS 8)
}

它能工作,但这样我就无法为消息和标题设置不同的大小。 - Libor Zapletal
这个可以工作,但是当点击一个动作时,字体会恢复到原始大小?你有这种情况吗? - Larry
我和 @Larry 有同样的问题,但我还没有找到解决方法...你呢? - alasker

12

使用UIAppearance协议。通过appearanceFont进行更多的hack,以改变UIAlertAction的字体。

UILabel创建一个类别。

UILabel + FontAppearance.h

@interface UILabel (FontAppearance)

@property (nonatomic, copy) UIFont * appearanceFont UI_APPEARANCE_SELECTOR;

@end

UILabel+FontAppearance.m

@implementation UILabel (FontAppearance)

- (void)setAppearanceFont:(UIFont *)font
{
    if (self.tag == 1001) {
        return;
    }

    BOOL isBold = (self.font.fontDescriptor.symbolicTraits & UIFontDescriptorTraitBold);
    const CGFloat* colors = CGColorGetComponents(self.textColor.CGColor);

    if (self.font.pointSize == 14) {
        // set font for UIAlertController title
        self.font = [UIFont systemFontOfSize:11];
    } else if (self.font.pointSize == 13) {
        // set font for UIAlertController message
        self.font = [UIFont systemFontOfSize:11];
    } else if (isBold) {
        // set font for UIAlertAction with UIAlertActionStyleCancel
        self.font = [UIFont systemFontOfSize:12];
    } else if ((*colors) == 1) {
        // set font for UIAlertAction with UIAlertActionStyleDestructive
        self.font = [UIFont systemFontOfSize:13];
    } else {
        // set font for UIAlertAction with UIAlertActionStyleDefault
        self.font = [UIFont systemFontOfSize:14];
    }
    self.tag = 1001;
}

- (UIFont *)appearanceFont
{
    return self.font;
}

@end

用法:

添加

[[UILabel appearanceWhenContainedIn:UIAlertController.class, nil] setAppearanceFont:nil];

AppDelegate.m中使用,使其适用于所有UIAlertController


在iOS 8.3中,标题的字号为13pt且加粗,因此我将条件更改为if (self.font.pointSize == 13 && isBold) { - Andrew Raphael
你提到要更改 UIAlertAction 的字体。但据我所知,UIAlertAction 并不使用 UILabel,而是使用 NSString。https://github.com/nst/iOS-Runtime-Headers/blob/3dde29deb7000cfac59d3e5473a71557123f29ea/Frameworks/UIKit.framework/UIAlertAction.h 我不知道如何自定义 NSString 的字体。 - peacetype
UIAlertAction根本不是视图类,它是描述操作的抽象类。视图本身是在UIAlertController内部生成的。因此,您设置包含在UIAlertController中的外观。 - mangerlahn

10

我在使用它。

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

添加一行代码(AppDelegate),使所有UIAlertController都能工作。


3
由于此方法已被弃用,请改用[[UIView appearanceWhenContainedInInstancesOfClasses:@[[UIAlertController class]]] setTintColor:newColor];。 - Peter Johnson

9

Swift 4

标题使用自定义字体的示例。 其他组件,如消息或操作,也同样适用。

    let titleAttributed = NSMutableAttributedString(
            string: Constant.Strings.cancelAbsence, 
            attributes: [NSAttributedStringKey.font:UIFont(name:"FONT_NAME",size: FONT_SIZE)]
    )

    let alertController = UIAlertController(
        title: "",
        message: "",
        preferredStyle: UIAlertControllerStyle.YOUR_STYLE
    )

    alertController.setValue(titleAttributed, forKey : "attributedTitle")
    present(alertController, animated: true, completion: nil)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接