Auto - 优雅的iPhone等比例精准适配工具(二. 可视化)

前言

很多时候我们苦于需要精准的适配各个屏幕尺寸的UI, 通常根据某一种倍数计算的结果并不能满足精准的需求, 随着iPhone设备不同尺寸的增加 这种需求更加迫切, 当然我说的这些都是属于对于产品细节要求苛刻 追求完美的那一部分, 如果你觉得随便适配适配看着还行就够了, 那么现在就可以X掉这个页面了.

问题

各种适配方案中, 针对不同尺寸iPhone适配的最佳方案莫过于等比例适配 (即按照基准屏幕宽度计算出一个比例值, 再按照这个比例值计算出其他宽度屏幕的值), 计算方法大家都会 加减乘除嘛, 但是如何可以优雅的封装 并在开发中更简单的使用就是一个问题了.

上一篇中我主要分享了等比例计算的封装思路和心得以及在代码布局层面的使用, 但是对于可视化布局方式来说, 通常则要将需要进行适配的约束或者控件拖线进来使用代码再次设置一番, 这显然是无奈之举, 既想用可视化又想等比例, 似乎是一件遥不可及的事情 (除非苹果爸爸在Xcode中增加这种特性).

这篇分享一下如何让 Storyboard / Xib 也能支持等比例适配.

解决方案

先说说思路吧.

首先 对于交换某些UI类的某些方法实现 增加等比例计算处理 这类的利用Method Swizzling的实现方案我并不赞同, 例如交换一下UILabel类的setFont方法的实现 增加一些将原有FontSize按照等比例计算的操作等等吧, 可能这类问题仁者见仁智者见智, 但我个人的观点是"对类本身的入侵性太强 极容易出现不可预知的问题" 试想各种工具类都利用Method Swizzling的方式来处理 那么你根本不清楚当你调用一个方法时被其他扩展进行了怎样的处理, 同时遇到Crash时 也很难定位罪魁祸首是谁.

Method Swizzling在我看来只适用于某些快速补救的情景, 如果过分依赖于它 那么整个项目会变得极不稳定, 它在某些情况下对整个项目的健壮性破坏是致命的.

我的思路:

上升一个维度, [斜眼笑]

乍一看似乎很难解决这种问题, 其实很简单, 我们换种方式思考, 可视化的控件对象里布局和属性设置都发生在什么时候?

没错, 初始化时, 它相当于一种对控件对象的归档 (本质是XML), 从Nib中解析出来并按照其中的配置初始化为对象, 也就是说可视化中的所有设置都相当于该对象的初始值, 我们只关心初始的值如何进行处理就好了, 这点很容易被忽略.

接下来就是确定哪些类的那些属性需要增加等比例计算的处理, 也不多, 我大概列一下:

  • NSLayoutConstraintconstant
  • UILabelfont, attributedText
  • UITextViewfont
  • UITextFieldfont
  • UIButton中各种statetitle, attributedTitle, image, backgroundImage, 还有3个EdgeInsets的属性

差不多这些就足够在 Storyboard / Xib 中影响布局了

说一说目标, 在不影响原有属性的情况下, 可以在 Storyboard / Xib 中进行选择性设置, 不想进行等比例适配的可以保持原样, 需要等比例适配的则可以在初始化后按照等比例计算好的值显示, 说到这里你是不是已经想到了? 就像一个开关, 某一个约束的constant需要等比例适配, 那么我们把这个约束的constant开关打开, 反之亦然.

extension NSLayoutConstraint {
    
    @IBInspectable private var autoConstant: Bool {
        get { return false }
        set {
            guard newValue else { return }
            
            constant = constant.auto()
        }
    }
}
constant

有没有恍然大悟? 通过这样的一个开关 对那些在 Storyboard / Xib 会影响到布局的属性逐一添加, 在 Storyboard / Xib 中根据需要开启, 达到在该控件对象初始化时将初始值进行等比例计算.

其他类的属性开关处理我就不一一解释了, 直接看源码更方便, 每个类都有自己的一些特性, 需要注意一下, 结合内部原理进行处理 (吐槽一下UIButton真麻烦)

类似上一篇中提到的AutoCalculationable协议, 这里也通过一个AutoAdapterable协议来支持使用者自定义计算逻辑.

protocol AutoAdapterable {
    func adapt(origin value: CGFloat) -> CGFloat
}

其实默认实现就是调用的上一篇介绍的auto()

extension AutoAdapterable {
    func adapt(origin value: CGFloat) -> CGFloat {
        return value.auto()
    }
}
auto

最后补充一下, @IBInspectable修饰的var属性的set方法执行时机, 在init?(coder aDecoder: NSCoder) 之后, func awakeFromNib()之前, 所以结合这个特点 应该可以帮助你避免一些不必要的麻烦.

总结

有些小工具虽然看起来没多少代码, 但却在某些场景中可以解决很大的问题, 我想分享的并非是这个工具, 而是创造工具的思想, 如何巧妙的解决问题, 或许你也和我一样想到了类似的方案, 那么先恭喜一下 英雄所见略同, 也希望你可以Review一下我的代码 为我提出更好的建议, 也许在我的代码中也有你没有想到的地方.

如果你有更好的想法 欢迎Issues留言讨论, 我是LEE, 下个轮子见.

上一篇 -> 等比例精准适配(一. 纯代码)

你可能感兴趣的:(Auto - 优雅的iPhone等比例精准适配工具(二. 可视化))