美文网首页
swift实现动态字体

swift实现动态字体

作者: 笑破天 | 来源:发表于2021-01-29 16:17 被阅读0次

一、期望

手动设置系统字体大小,App里面的字体能跟随发生变化,并有最大最小字体限制。

二、方案

1、App内生效和重启App生效

1. 需要重启app才能生效(快手)

借助runtime,在app启动的时候交换方法实现。
特点:需要重启app才能生效。无侵入,只需要添加一个UIFont分类,并在appdelegate里面调用交换方法。对整体项目改动小。

2. app内实时刷新(微信、QQ)

① App内设置字体大小,选择字体后,发送通知。切回到你需要改变的界面上时,在收到通知的方法里改变当前界面字体。(微信)
② 调整系统字体大小,切换到App内,字体大小自动更改,用DynamicType实现(QQ)
特点:app内实时刷新。可以整体替换,第三方重新pod,但项目整体改动较多
③ 保存字体大小到本地,所有用到字体的地方实时获取或刷新

采用:app内实时刷新

2、全局替换和部分替换

2.1 整体全部手动替换,搜索所有的systemFont,改为xx_systemFont,第三方重新pod,但项目整体改动较多,有风险
2.2 几个核心页面做下适配,其他不管

采用:6个核心页面做下适配

三、代码实现

1、重启app生效(快手)

添加一个UIFont分类,更改systemFont类方法的实现

UIFont.swizzleSystemFont()
extension UIFont {
    class func swizzleSystemFont() {
        let cls: AnyClass? = object_getClass(self)
        if let systemMethod = class_getInstanceMethod(cls, #selector(UIFont.systemFont(ofSize:))), let swizzMethod = class_getInstanceMethod(cls, #selector(UIFont.my_systemFont(ofSize:))) {
            method_exchangeImplementations(systemMethod, swizzMethod);
        }
        ...
    }
// 最大字号18,最小字号12
    class func my_finalFontSize(ofSize: CGFloat) -> CGFloat {
        // 计算出 body 类型比默认的大小要变化了多少,然后在 pointSize 的基础上叠加这个变化
        let font = UIFont.preferredFont(forTextStyle: TextStyle.body)
        let offsetPointSize = font.pointSize - 17 // default UIFontTextStyleBody fontSize is 17
        var finalPointSize = ofSize + offsetPointSize
        finalPointSize = max(min(finalPointSize, 18), 12)
        return finalPointSize
    }
@objc class func my_systemFont(ofSize: CGFloat) -> UIFont {
        let finalPointSize = my_finalFontSize(ofSize: ofSize)
        return my_systemFont(ofSize: finalPointSize)
    }
// 不用runtime专用方法
class func xx_systemFont(ofSize: CGFloat) -> UIFont {
        let finalPointSize = my_finalFontSize(ofSize: ofSize)
        return UIFont.systemFont(ofSize: finalPointSize)
    }
2、App内实时刷新

1.注册一个字体改变的通知。选择字体后,发送通知。切回到你需要改变的界面上时,在收到通知的方法里改变当前界面字体。(微信)
2.用DynamicType实现(QQ)
效果:调整系统字体大小,切换到App内,字体大小自动更改
缺点:字体大小只能是给定的10种类型之一

// 收到系统字体大小改变的通知刷新对应的UI字体
NotificationCenter.default.addObserver(self, selector: #selector(fontChange), name: UIContentSizeCategory.didChangeNotification, object: nil)
@objc func fontChange() {
        let font = UIFont.preferredFont(forTextStyle: .body)
        testLab.font = UIFont.systemFont(ofSize: font.pointSize)
// 这里调用setNeedsLayout做一个需要重新布局的标记,在下一个draw周期(60Hz)自动重绘
}

// iOS10之后,系统字体改变不需要接受通知,设置adjustsFontForContentSizeCategory即可自动变化
testLab.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)
testLab.adjustsFontForContentSizeCategory = true
// 必要的时候更新frame

3.保存字体大小到本地,所有用到字体的地方实时获取或刷新

3、最终方案:

全局替换系统字体方法,会有个问题:很多地方view大小没做适配。采用适配4、5个核心页面的方案。
实现方法:给UIFont分类添加新的方法,需要适配的地方把systemFont改为xx_systemFont即可
特点:哪里需要适配就改哪里,灵活度和自由度自己控制

extension UIFont {
  class func xx_systemFont(ofSize: CGFloat) -> UIFont {
        let finalPointSize = my_finalFontSize(ofSize: ofSize)
        return UIFont.systemFont(ofSize: finalPointSize)
    }
}
label.font = UIFont.xx_systemFont(ofSize: 16)

四、遇到问题

1、Method 'initialize()' defines Objective C class method 'initialize', which is not permitted by Swift
Swift4开始废弃load方法和initital方法了,写个类方法交换实现,然后直接在appdelegate里面调用类方法即可。UIButton可行,但是UIFont不可行(UIFont是类方法,写错了就不生效)。
2、UIFont的systemFont设置了没生效,因为systemFont是类方法,写错了
3、百度到的一些方案遇到些问题UILabel的font方法获取不到,willMove(toSuperview:)会出现UIButtonLabel的情况,属性字符串的兼容情况等。

iOS10的一些玩意儿:Dynamic Type
Swift黑魔法 - Runtime

相关文章

网友评论

      本文标题:swift实现动态字体

      本文链接:https://www.haomeiwen.com/subject/wvaazktx.html