在整个iOS应用程序中使用自定义字体(Swift)

29

我知道要在屏幕上设置一个元素的自定义字体,可以简单地执行someLabel.font=UIFont(name: "Exo 2.0", size: 15)

我想知道如何使用swift为整个应用设置自定义字体。(一个hack是对应用程序的每个元素都进行相同的操作,但这只会导致可维护性变得非常差。)

我看到这个问题已经被问过,带有objective-C标记How to set a custom font for entire iOS app without specifying size但我对objective-C不够熟悉,无法将代码转换成swift。


你可以编写一个 UILabel 扩展,以返回你想要的字体,代替其内部字体属性。 - Ian MacDonald
5个回答

37

您可以设置UILabel和其他UIView的外观:

UILabel.appearance().font = UIFont(name: "yourFont", size: yourSize)

更一般化:

AnyUIView.appearance().font = UIFont(name: "yourFont", size: yourSize)

42
我的问题是,如果我实施了这个方法,字体大小也会被覆盖吗?如何避免这种情况? - Krutarth Patel
3
这个很好用,可以避免覆盖字体大小! - Ali Momen Sani
2
有没有一种方法可以像这样全局设置字体,而不指定大小?这样,如果我有一个带有副标题样式的表格单元格,文本仍将以所选字体的默认大小显示,适用于这些样式。 - Jonathan Tuzman
@KrutarthPatel 请看下面我的解决方案。字体大小没有被覆盖。 - Rmalmoe

24

作为更详细的回答,我建议使用扩展程序来获得以下好处:

  • 没有大小覆盖(无论您在设计师中设置什么都将被使用)
  • 没有样式覆盖(Bold、Light、Medium、UltraLight 已在我的代码中实现,但您可以根据需要自定义它)
import UIKit

extension UILabel {
    @objc var substituteFontName : String {
        get {
            return self.font.fontName;
        }
        set {
            let fontNameToTest = self.font.fontName.lowercased();
            var fontName = newValue;
            if fontNameToTest.range(of: "bold") != nil {
                fontName += "-Bold";
            } else if fontNameToTest.range(of: "medium") != nil {
                fontName += "-Medium";
            } else if fontNameToTest.range(of: "light") != nil {
                fontName += "-Light";
            } else if fontNameToTest.range(of: "ultralight") != nil {
                fontName += "-UltraLight";
            }
            self.font = UIFont(name: fontName, size: self.font.pointSize)
        }
    }
}

extension UITextView {
    @objc var substituteFontName : String {
        get {
            return self.font?.fontName ?? "";
        }
        set {
            let fontNameToTest = self.font?.fontName.lowercased() ?? "";
            var fontName = newValue;
            if fontNameToTest.range(of: "bold") != nil {
                fontName += "-Bold";
            } else if fontNameToTest.range(of: "medium") != nil {
                fontName += "-Medium";
            } else if fontNameToTest.range(of: "light") != nil {
                fontName += "-Light";
            } else if fontNameToTest.range(of: "ultralight") != nil {
                fontName += "-UltraLight";
            }
            self.font = UIFont(name: fontName, size: self.font?.pointSize ?? 17)
        }
    }
}

extension UITextField {
    @objc var substituteFontName : String {
        get {
            return self.font?.fontName ?? "";
        }
        set {
            let fontNameToTest = self.font?.fontName.lowercased() ?? "";
            var fontName = newValue;
            if fontNameToTest.range(of: "bold") != nil {
                fontName += "-Bold";
            } else if fontNameToTest.range(of: "medium") != nil {
                fontName += "-Medium";
            } else if fontNameToTest.range(of: "light") != nil {
                fontName += "-Light";
            } else if fontNameToTest.range(of: "ultralight") != nil {
                fontName += "-UltraLight";
            }
            self.font = UIFont(name: fontName, size: self.font?.pointSize ?? 17)
        }
    }
}

使用扩展的示例:

例如,将这些行放在您的起始控制器viewDidLoad中

UILabel.appearance().substituteFontName = "IRANSans"; // USE YOUR FONT NAME INSTEAD
UITextView.appearance().substituteFontName = "IRANSans"; // USE YOUR FONT NAME INSTEAD
UITextField.appearance().substituteFontName = "IRANSans"; // USE YOUR FONT NAME INSTEAD

P.S. 正如 @Christian 提到的,您可以为几乎任何 UIView 编写自己的扩展。


3
这对于iOS 11和Swift 4仍然有效吗?我在UILabel中使用self.font.fontName时得到了nil。 - Tj3n
我会检查并尽快告诉您结果。 - Mostafa Aghajani
谢谢,请在您方便的时候留下评论。 - Tj3n
2
更新:您需要在substituteFontName中添加@objc以保持与Swift4的兼容性。 - Federico Malagoni
2
如何使用相同的概念全局替换属性文本字体? - Aakash Gupta
显示剩余2条评论

5

终于搞明白了。我找到了最简洁的方法。(Swift 4) 这个解决方案不需要设置字体大小,也不会覆盖所有字体大小。

  UILabel.appearance().font = UIFont.preferredFont(forTextStyle: UIFontTextStyle(rawValue: "Roboto"))

对于那些想要知道在哪里放置这段代码的人,我将其放在了我的AppDelegate.swift文件中,放在了

标签内部。
 func application(_ application: UIApplication, 
 didFinishLaunchingWithOptions...

Swift 4.2

 UILabel.appearance().font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle(rawValue: "Roboto"))

1
@DanielBeltrami 你可以将这段代码放在你的应用程序委托中,放在以下方法内...func application(_ application: UIApplication, didFinishLaunchingWithOptions 这样就可以在整个应用程序中使用了。 - Rmalmoe
4
似乎在iOS 12和Swift 4.2上不能正常工作,大多数文本字体大小会变得比应该的要小。 - Ido
当然是在didFinishLaunchingWithOptions中。但没关系,我找到了更好的解决方案来改变我应用程序的整个字体:https://dev59.com/12oy5IYBdhLWcg3wMLSj#40484460 - Ido
1
不起作用。在Swift 5中,覆盖了所有标签的字体大小。 - spnkr
答案明确说明了Swift 4.2。因此,如果您不是在谈论相同的版本,则“不起作用”并不是实际上的正确响应。@spnkr - Rmalmoe

2

我发现在使用iOS 11的Swift 4中实现同样的功能有一种方法。

只需要在变量名之前添加@objc关键字即可。因此,变量声明将如下所示:

@objc public var substituteFontName : String {
    get {}
    set {}
}

希望这能帮助其他遇到这个问题的人。

我曾经遇到过这个问题,浪费了我超过两天的时间来解决它!谢谢。 - Hadi Sharghi

0
答案是之前提供的答案的组合,但我不得不尝试一些组合才能在iOS 13中使其工作:
所有这些都放置在表视图控制器中,该控制器被设计为符合UIFontPickerViewControllerDelegate,以便为用户提供选择字体的视图。 这将使用新字体更新所有UILabel,但保留其他属性。
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath)

    if cell === fontCell {
        if #available(iOS 13, *) {
            let configuration = UIFontPickerViewController.Configuration()
            configuration.includeFaces = true

            let fontVC = UIFontPickerViewController(configuration: configuration)
            fontVC.delegate = self

            present(fontVC, animated: true)
        } else {
            let ac = UIAlertController(title: "iOS 13 is required", message: "Custom fonts are only supported by devices running iOS 13 or above.", preferredStyle: .alert)
            ac.addAction(UIAlertAction(title: "OK", style: .default))
            present(ac, animated: true)
        }
    }
}

}

@available(iOS 13.0, *)
extension AppearanceTableViewController: UIFontPickerViewControllerDelegate {
    func fontPickerViewControllerDidPickFont(_ viewController: UIFontPickerViewController) {
        // attempt to read the selected font descriptor, but exit quietly if that fails
        guard let descriptor = viewController.selectedFontDescriptor else { return }

        let font = UIFont(descriptor: descriptor, size: 20)
        UILabel.appearance().substituteFontName = font.fontName
    }
}

extension UILabel {
    @objc public var substituteFontName : String {
        get {
            return self.font.fontName;
        }
        set {
            let fontNameToTest = self.font.fontName.lowercased()
            var fontName = newValue
            if fontNameToTest.range(of: "bold") != nil {
                fontName += "-Bold"
            } else if fontNameToTest.range(of: "medium") != nil {
                fontName += "-Medium"
            } else if fontNameToTest.range(of: "light") != nil {
                fontName += "-Light"
            } else if fontNameToTest.range(of: "ultralight") != nil {
                fontName += "-UltraLight"
            }
            self.font = UIFont(name: fontName, size: self.font.pointSize)
        }
    }
}

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