分类归档 iOS开发

通过guhuangwudi@gmail.com

如何设计一个自动布局库:以SDAutoLayout为例(转)

前言:SDAutoLayout 在发布之后受到了众多iOS开发者的青睐和支持,不到半年时间内在GitHub上已经获得2000+star,同时被众多公司和个人开发者作为开发首选自动布局库。现在,以SDAutoLayout为例简单介绍一下如何设计一个自动布局库。

所谓自动布局,其实就是将手动布局的计算过程抽象出一套算法,然后利用约束模型收集view的宽高左右等各个维度对应的数据,最后在恰当的时机根据约束计算出view的frame。

了解了这个基本原理之后,我们就可以动手设计一个自动布局库了。在设计自动布局库的过程中,我们主要需要解决以下几个问题:

1.何时进行自动布局相关运算?

2.如何实现自动布局算法?

3.如何设计约束模型管理机制?

4.如何设计一套简洁的连式语法API?(此步非必须项,仅供有兴趣者参考,以SDAutoLayout为例)

5.如何实现自动计算cell高度?

首先,什么时候进行自动布局相关运算才最合适呢?我们知道,当一个view的frame发生改变或者由于其他情况需要调整子view就会触发 layoutSubvies方法,在手动布局时我们经常通过重写这个方法来实现对子view的调整。但是我们要设计自动布局库时,显然不应该强行重写 view的layoutSubvies方法,这样就会破坏了开发者对view的自主监听和掌控,因此,layoutSubvies方法执行完毕之后才是我 们进行自动布局相关运算的最佳时机。好的,那么究竟如何监控这个“最佳时机”的到来呢?此时我想熟悉OC运行时的同学已经想到了,没错,就是利用OC的黑 魔法“Method Swizzling”即可完美解决,代码如下(源码地址 ):

交换layoutSubvies方法

2.如何实现自动布局算法?

当解决了自动布局运算时机的问题之后,我们面临的下一个问题就是如何设计自动布局算法,这也是整个自动布局库最核心的部分。

在讲这个布局算法之前,有必要先说一下我总结出的一个自动布局算法要素:先计算绝对属性,后计算相对属性。

那么,何为“相对属性”?相对属性就是需要参照于其他物体才有意义的属性。比如我们会说“河南在河北的南面”,那么这个“南”就是一个相对属性,有了河北 作为参照,这个“南”才是有意义的,如果你只说“河南在南面”,那么这个“南”就是没有意义的。在自动布局中,一个view的x、y、centerX、 centerY、left、right等属性就属于这种相对属性。

对比“相对属性”,“绝对属性”就很好理解了,所谓“绝对属性”就是不依赖于参照物改变而改变的属性。比如我会说“iPhone6 是4.7英寸屏幕”,那么这个“4.7英寸”就是绝对属性,他不会因为和其他参照物比较而改变。在自动布局中,一个view的width、height属 性就是这种绝对属性。

另外,在针对每个“相对属性”计算过程中,“宽高校验”是非常有必要的,如果宽高是不准确的,那么计算出来的right、bottom、centerX、centerY等属性值也肯定是不准确的。示例代码如下(源码地址):

自动布局

right自动布局方法实现

3.如何设计约束模型管理机制?

然后,我们还要设计一下自动布局约束管理机制,也就是说,每个view的约束该交由谁来管理呢?是view自己?还是view的superView呢?结 合刚刚对问题一的分析,既然是父view在调用layoutSubvies方法之后再进行自动布局计算,那么让父view来管理所有子view的约束就再 合理不过了。在SDAutoLayout中,每个view都有一个autoLayoutModelsArray数组来管理子view的约束,子view在 调用sd_layout方法时候会初始化一个约束模型并添加到其父view的autoLayoutModelsArray数组中,这就是为什么在使用 SDAutoLayout过程中要先将子view添加到父view然后再做布局设置的原因了。示例代码如下(源码地址):

初始化约束模型

4.如何设计一套简洁的连式语法API?

链式语法以其简洁明了的优点受到了众多开发者的推崇,在SDAutoLayout库中,约束模型将各种布局数据设置的操作封装进一个个block中,每次 置一个维度的约束时实际上是调用了这个维度对应的block,把相关参数以(参数1,参数2)的形式传递给block进行相关设置,然后block每次把 约束模型自身作为返回结果传递下去,这样就可以再次用”.”来调用其他维度约束对应的block。示例代码如下(源码地址):

设置约束

每个维度约束对应的block

内部实现

5.如何实现自动计算cell高度?

SDAutoLayout为开发者提供了简洁高效的cell高度自动计算方法,使用者只需调用一行代码“[yourCell setupAutoHeightWithBottomView:bottomView bottomMargin:bottomMargin];”即可轻松实现cell高度自动计。

为了实现此功能,SDAutoLayout库在内部建立一个和你的cell一样的模型,然后把你传递过来的model数据赋值给模型cell,设置完成后 调用“[self.modelCell.contentView layoutSubviews]”方法来计算cell的真实高度然后返回给你的tableView,同时还会建立cell高度缓存库以供 tableView滚动时直接返回cell高度而不必再次计算,如果有需要,你也可以开启cell的Frame缓存机制,这样就会在你的cell出现的时 候直接给cell内部控件设置frame而不必时时计算调整,从而大大增加了滚动流畅度。示例代码如下(源码地址):

开启cell的frame缓存

返回cell高度

返回并缓存cell高度