使用Swift 4根据当前字体重量更改应用程序中所有UILabel的字体

6
在Swift 3中,我在AppDelegate中使用以下代码将所有粗体字替换为中等字体:
UILabel.appearance().substituteFontNameBold = "HelveticaNeue-Medium"

通过将此扩展添加到UILabel:

extension UILabel {
    var substituteFontNameBold : String {
        get { return self.font.fontName }
        set {
            if self.font.fontName.contains("Bold") {
                self.font = UIFont(name: newValue, size: self.font.pointSize)
            }
        }
    }
}

在升级到Swift 4之后,我在

处遇到了“意外地发现了一个空值,无法解包可选值的致命错误”。
if self.font.fontName.contains("Bold") {

如何在Swift 4中继续将标签中所有粗体字替换为中等字体?


很可能此时的 self (UILabel) 为空。尝试在稍后的阶段调用它。 - jonaszmclaren
由于某些原因,一些答案以及我对这些答案的回复被删除了(oO?): 那个人建议使用UILabel.appearance()替换self:但此举并未解决问题。 - Charles Rostaing
基于 Sandy Chapman 的回答:https://dev59.com/12oy5IYBdhLWcg3wMLSj#28440649在 Swift 3 上完全可用。 在 Swift 4 上:self.font 返回 nil。 - Charles Rostaing
我遇到了完全相同的问题。我尝试在viewDidAppear中执行它,但是self.font.pointSize仍然为nil值。 - Nabeel
放弃了。我通过Ctrl-F直接更改了Storyboard中的所有字体... - Charles Rostaing
现在遇到了同样的问题,不太喜欢子类或手动更改...但这已经不再起作用了,很奇怪。 - Tj3n
3个回答

7

Swift 4.0

这将帮助您为所有UILabel、UIButton等设置自定义字体。只需创建一个扩展并替换为此代码即可:

var appFontName       = "Gudea" //Please put your REGULAR font name
var appFontBoldName   = "Gudea" //Please put your BOLD font name
var appFontItalicName = "Gudea" //Please put your ITALIC font name

extension UIFont {

@objc class func mySystemFont(ofSize size: CGFloat) -> UIFont {
    return UIFont(name: appFontName, size: size)!
}

@objc class func myBoldSystemFont(ofSize size: CGFloat) -> UIFont {
    return UIFont(name: appFontBoldName, size: size)!
}

@objc class func myItalicSystemFont(ofSize size: CGFloat) -> UIFont {
    return UIFont(name: appFontItalicName, size: size)!
}

@objc convenience init(myCoder aDecoder: NSCoder) {
    if let fontDescriptor = aDecoder.decodeObject(forKey: "UIFontDescriptor") as? UIFontDescriptor {
        if let fontAttribute = fontDescriptor.fontAttributes[UIFontDescriptor.AttributeName(rawValue: "NSCTFontUIUsageAttribute")] as? String {
            var fontName = ""
            switch fontAttribute {
            case "CTFontRegularUsage":
                fontName = appFontName
            case "CTFontEmphasizedUsage", "CTFontBoldUsage":
                fontName = appFontBoldName
            case "CTFontObliqueUsage":
                fontName = appFontItalicName
            default:
                fontName = appFontName
            }
            self.init(name: fontName, size: fontDescriptor.pointSize)!
        }
        else {
            self.init(myCoder: aDecoder)
        }
    }
    else {
        self.init(myCoder: aDecoder)
    }
}

class func overrideInitialize() {
    if self == UIFont.self {
        let systemFontMethod = class_getClassMethod(self, #selector(systemFont(ofSize:)))
        let mySystemFontMethod = class_getClassMethod(self, #selector(mySystemFont(ofSize:)))
        method_exchangeImplementations(systemFontMethod!, mySystemFontMethod!)

        let boldSystemFontMethod = class_getClassMethod(self, #selector(boldSystemFont(ofSize:)))
        let myBoldSystemFontMethod = class_getClassMethod(self, #selector(myBoldSystemFont(ofSize:)))
        method_exchangeImplementations(boldSystemFontMethod!, myBoldSystemFontMethod!)

        let italicSystemFontMethod = class_getClassMethod(self, #selector(italicSystemFont(ofSize:)))
        let myItalicSystemFontMethod = class_getClassMethod(self, #selector(myItalicSystemFont(ofSize:)))
        method_exchangeImplementations(italicSystemFontMethod!, myItalicSystemFontMethod!)

        let initCoderMethod = class_getInstanceMethod(self, #selector(UIFontDescriptor.init(coder:))) // Trick to get over the lack of UIFont.init(coder:))
        let myInitCoderMethod = class_getInstanceMethod(self, #selector(UIFont.init(myCoder:)))
        method_exchangeImplementations(initCoderMethod!, myInitCoderMethod!)
    }
}
}

更新:Swift3.2

请将此行替换为:

if let fontAttribute = fontDescriptor.fontAttributes[UIFontDescriptor.AttributeName(rawValue: "NSCTFontUIUsageAttribute")] as? String {

使用:

if let fontAttribute = fontDescriptor.fontAttributes["NSCTFontUIUsageAttribute"] as? String {

无论如何,block if self == UIFont.self {} 是无用的。 - Danyl

6
我已经找到了解决方法,你基本上需要添加@objc才能使它正常工作,添加后就不会再出现空值了。
@objc public var substituteFontName : String

谢谢!我真的不太理解为什么,但它确实有效。你有什么理论可以解释这个吗?@Tj3n - barley
1
@barley 看起来 UIAppearance 只能读取那些暴露在 objc 运行时中的函数(可能是通过 KVC 实现的),关键字就是这个。也许苹果还没有在 Swift 中添加对此功能的支持。 - Tj3n
嗯,是的,这有点让我信服,但也有点不确定... UIApperance 肯定可以读取 substituteFontName,因为它在其中运行了 set 方法。但如果 substituteFontName 没有作为 Obj-C 接口公开,它就无法访问 UILabel.font 属性。 - barley

2
这可以通过以下方式覆盖系统字体来实现:

这可以通过以下方式覆盖系统字体来实现:

import UIKit

struct AppFontName {
    static let regular = "DINNextLTArabic-Regular"
}

extension UIFontDescriptor.AttributeName {
    static let nsctFontUIUsage =
        UIFontDescriptor.AttributeName(rawValue: "NSCTFontUIUsageAttribute")
}

extension UIFont {

    @objc class func mySystemFont(ofSize size: CGFloat) -> UIFont {
        return UIFont(name: AppFontName.regular, size: size)!
    }

    @objc convenience init(myCoder aDecoder: NSCoder) {
        if let fontDescriptor = aDecoder.decodeObject(forKey: "UIFontDescriptor") as? UIFontDescriptor {
                let fontName = AppFontName.regular
                self.init(name: fontName, size: fontDescriptor.pointSize)!

        } else {
            self.init(myCoder: aDecoder)
        }
    }

    class func overrideInitialize() {
        if self == UIFont.self {
            let systemFontMethod = class_getClassMethod(self, #selector(systemFont(ofSize:)))
            let mySystemFontMethod = class_getClassMethod(self, #selector(mySystemFont(ofSize:)))
            method_exchangeImplementations(systemFontMethod!, mySystemFontMethod!)


            let initCoderMethod = class_getInstanceMethod(self, #selector(UIFontDescriptor.init(coder:))) // Trick to get over the lack of UIFont.init(coder:))
            let myInitCoderMethod = class_getInstanceMethod(self, #selector(UIFont.init(myCoder:)))
            method_exchangeImplementations(initCoderMethod!, myInitCoderMethod!)
        }
    }
}

并且在 appDelegate 中

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {

    UIFont.overrideInitialize()

    return true
}

谢谢帮忙。无论如何,block if self == UIFont.self {} 是无用的。 - Danyl
完美的工作!适用于入门应用程序,非常感谢! - Ing. Ron

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