Auto convert systemFont to AppFont in Swift - TiepNC/Note GitHub Wiki

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
   AppFont.config()
   ...
}

File AppFont.swift


import UIKit

final class AppFont {
    
    static func config() {
        UIFont.overrideInitialize()
    }
}

private enum AppFontName: String {
    case regular = "Roboto-Regular"
    case medium = "Roboto-Medium"
    case bold = "Roboto-Bold"
    case italic = "Roboto-Italic"
}

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

private extension UIFont {
    
    @objc convenience init(myCoder aDecoder: NSCoder) {
        guard let fontDescriptor = aDecoder.decodeObject(forKey: "UIFontDescriptor") as? UIFontDescriptor,
            let fontAttribute = fontDescriptor.fontAttributes[.nsctFontUIUsage] as? String else {
                self.init(myCoder: aDecoder)
                return
        }
        let appFontName: AppFontName
        switch fontAttribute {
        case "CTFontRegularUsage":
            appFontName = AppFontName.regular
        case "CTFontMediumUsage":
            appFontName = AppFontName.medium
        case "CTFontBoldUsage":
            appFontName = AppFontName.bold
        case "CTFontObliqueUsage":
            appFontName = AppFontName.italic
        default:
            appFontName = AppFontName.regular
        }
        self.init(name: appFontName.rawValue, size: fontDescriptor.pointSize)!
    }
    
    class func overrideInitialize() {
        guard self == UIFont.self else { return }
        
        if let systemFontMethod = class_getClassMethod(self, #selector(systemFont(ofSize:))),
            let mySystemFontMethod = class_getClassMethod(self, #selector(mySystemFont(ofSize:))) {
            method_exchangeImplementations(systemFontMethod, mySystemFontMethod)
        }
        if let boldSystemFontMethod = class_getClassMethod(self, #selector(boldSystemFont(ofSize:))),
            let myBoldSystemFontMethod = class_getClassMethod(self, #selector(myBoldSystemFont(ofSize:))) {
            method_exchangeImplementations(boldSystemFontMethod, myBoldSystemFontMethod)
        }
        if let italicSystemFontMethod = class_getClassMethod(self, #selector(italicSystemFont(ofSize:))),
            let myItalicSystemFontMethod = class_getClassMethod(self, #selector(myItalicSystemFont(ofSize:))) {
            method_exchangeImplementations(italicSystemFontMethod, myItalicSystemFontMethod)
        }
        if let systemFontMethod = class_getClassMethod(self, #selector(systemFont(ofSize:weight:))),
            let mySystemFontMethod = class_getClassMethod(self, #selector(mySystemFont(ofSize:weight:))) {
            method_exchangeImplementations(systemFontMethod, mySystemFontMethod)
        }
        // Trick to get over the lack of UIFont.init(coder:))
        if let initCoderMethod = class_getInstanceMethod(self, #selector(UIFontDescriptor.init(coder:))),
            let myInitCoderMethod = class_getInstanceMethod(self, #selector(UIFont.init(myCoder:))) {
            method_exchangeImplementations(initCoderMethod, myInitCoderMethod)
        }
    }
    
    @objc
    class func mySystemFont(ofSize size: CGFloat) -> UIFont {
        return UIFont(name: AppFontName.regular.rawValue, size: size)!
    }
    
    @objc
    class func mySystemFont(ofSize size: CGFloat, weight: UIFont.Weight) -> UIFont {
        let appFontName: AppFontName
        switch weight {
        case .regular:
            appFontName = AppFontName.regular
        case .medium:
           appFontName = AppFontName.medium
        case .bold:
            appFontName = AppFontName.bold
        default:
            appFontName = AppFontName.italic
        }
        return UIFont(name: appFontName.rawValue, size: size)!
    }
    
    @objc
    class func myBoldSystemFont(ofSize size: CGFloat) -> UIFont {
        return UIFont(name: AppFontName.bold.rawValue, size: size)!
    }
    
    @objc
    class func myItalicSystemFont(ofSize size: CGFloat) -> UIFont {
        return UIFont(name: AppFontName.italic.rawValue, size: size)!
    }
}