Auto Layout (3)

简介: Auto Layout 是怎么进行自动布局的,性能如何?

Auto Layout 是怎么进行自动布局的,性能如何?


Auto Layout 到底是如何实现自动布局的,这种布局算法真的会影响性能吗?


微信图片_20220510044935.png

image.png


  • 一个是 1997 年,Auto Layout 用到的布局算法 Cassowary 被发明了出来;
  • 另一个是 2011 年,苹果公司将 Cassowary 算法运用到了自家的布局引擎 Auto Layout中。


Auto Layout 的生命周期


Auto Layout 不只有布局算法 Cassoway,还包含了布局在运行时的生命周期等一整套布局引擎系统,用来统一管理布局的创建、更新和销毁。了解 Auto Layout 的生命周期,是理解它的性能相关话题的基础。这样,在遇到问题,特别是性能问题时,我们才能从根儿上找到原因,从而避免或改进类似的问题。


这一整套布局引擎系统叫作 Layout Engine ,是 Auto Layout 的核心,主导着整个界面布局。


每个视图在得到自己的布局之前,Layout Engine 会将视图、约束、优先级、固定大小通过计算转换成最终的大小和位置。在 Layout Engine 里,每当约束发生变化,就会触发 DefferedLayout Pass,完成后进入监听约束变化的状态。当再次监听到约束变化,即进入下一轮循环中。整个过程如下图所示:


微信图片_20220510044941.png

image.png


  • Constraints Change 表示的就是约束变化,添加、删除视图时会触发约束变化。Activating 或 Deactivating,设置 Constant 或 Priority 时也会触发约束变化。Layout Engine在碰到约束变化后会重新计算布局,获取到布局后调用superview.setNeedLayout(),然后进入Deferred Layout Pass。


  • Deferred Layout Pass 的主要作用是做容错处理。如果有些视图在更新约束时没有确定或缺失布局声明的话,会先在这里做容错处理。
  • Layout Engine 会从上到下调用 layoutSubviews() ,通过 Cassowary 算法计算各个子视图的位置,算出来后将子视图的 frame 从 Layout Engine 里拷贝出来。


iOS 12提高了Auto Layout性能, 怎么做到的呢?


iOS之前的性能问题


微信图片_20220510044945.png

image.png


上图是 WWDC 2018 中 202 Session 里讲到的 Auto Layout 在 iOS 12 中优化后的表现。可以看到,优化后的性能,已经基本和手写布局一样可以达到性能随着视图嵌套的数量呈线性增长了。而在此之前的 Auto Layout,视图嵌套的数量对性能的影响是呈指数级增长的。


在 1997 年时,Cassowary 是以高效的界面线性方程求解算法被提出来的。它解决的是界面的线性规划问题,而线性规划问题的解法是 Simplex 算法。单从 Simplex 算法的复杂度来看,多数情况下是没有指数时间复杂度的。而 Cassowary 算法又是在 Simplex 算法基础上对界面关系方程进行了高效的添加、修改更新操作,不会带来时间复杂度呈指数级增长的问题。


微信图片_20220510044950.png

image.png


由此可知, iOS12之前Auto Layout 并没有用上 Cassowary 高效修改更新的特性。


iOS12 之前,很多约束变化时都会重新创建一个计算引擎 NSISEnginer 将约束关系重新加进来,然后重新计算。结果就是,涉及到的约束关系变多时,新的计算引擎需要重新计算,最终导致计算量呈指数级增加。


iOS12 的 Auto Layout 更多地利用了 Cassowary 算法的界面更新策略,使其真正完成了高效的界面线性策略计算。


Auto Layout 的易用性


苹果公司其实也考虑到了这点。所以,苹果公司后来又提供了 VFL (Visual Format Language)这种 DSL(Domain Specific Language,中文可翻译为“领域特定语言”) 语言来简化 AutoLayout 的写法。


Auto Layout 只是一种最基础的布局思路。在前端出现了 Flexbox 这种高级的响应式布局思路后,苹果公司也紧跟其后,基于 Auto Layout 又封装了一个类似 Flexbox的UIStackView,用来提高 iOS 开发响应式布局的易用性。


微信图片_20220510044954.png

image.png


这样做,可以极大地减少你在约束关系设置上所做的重复工作,提升页面布局的体验。


DSL参考链接


你是应该选择继续手动布局还是选择 Auto Layout 呢?


今天这篇文章,我和你说了 Auto Layout 背后使用的 Cassowary 算法。同时,我也和你说了苹果公司经过一番努力,终于在 iOS 12 上用到了 Cassowary 算法的界面更新策略,使得 AutoLayout 的性能得到了大幅提升。


至此,Auto Layout 性能的提升可以让你放心地使用。


记得上次我和一个苹果公司的技术支持人员聊到,到底应该使用苹果自己的布局还是第三方工具比如 Texture 时,他的观点是:使用苹果公司的技术得到的技术升级是持续的,而第三方不再维护的可能性是很高的。


其实细细想来,这非常有道理。这次 Auto Layout 的升级就是一个很好的例子,你的代码一行不变就能享受到耗时从指数级下降到线性的性能提升。而很多第三方库,会随着 iOS 系统升级失去原有的优势。


文章参考链接汇总


  1. 极客时间->戴铭03-Auto-Layout-是怎么进行自动布局的,性能如何?
  2. DSL参考链接
  3. 使用 Auto Layout 一定要注意多使用 Compression Resistance Priority 和 Hugging Priority,利用优先级的设置,让布局更加灵活,代码更少,更易于维护。Demo
  4. cell 里面 label的高度自适应问题,这个在layout应该使用频率比较高吧,


  1. Masonry参考1
  2. autolayout参考2


cell 里面 label的高度自适应问题


主要是UILabel的高度会有变化,所以这里主要是说说label变化时如何处理,设置UILabel的时

候注意要设置preferredMaxLayoutWidth这个宽度,还有ContentHuggingPriority为

UILayoutPriorityRequried


textLabel = [UILabel new];
textLabel.numberOfLines = 0;
textLabel.preferredMaxLayoutWidth = maxWidth;
[self.contentView addSubview:textLabel];
[textLabel mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(statusView.mas_bottom).with.offset(10); 
    make.left.equalTo(self.contentView).with.offset(10); 
    make.right.equalTo(self.contentView).with.offset(-10); 
    make.bottom.equalTo(self.contentView).with.offset(-10); 
}];
[_contentLabel setContentHuggingPriority:UILayoutPriorityRequiredforAxis:UILayoutConstraintAxisVertical];


如果版本支持最低版本为iOS 8以上的话可以直接利用UITableViewAutomaticDimension在

tableview的heightForRowAtIndexPath直接返回即可。


tableView.estimatedRowHeight = 80; //减少第一次计算量,iOS7后支持 
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath
*)indexPath { 
// 只用返回这个! 
return UITableViewAutomaticDimension; 
}


但如果需要兼容iOS 8之前版本的话,就要回到老路子上了,主要是用

systemLayoutSizeFittingSize来取高。步骤是先在数据model中添加一个height的属性用来缓存高,然后在table view的heightForRowAtIndexPath代理里static一个只初始化一次的Cell实例,然后根据model内容填充数据,最后根据cell的contentView的

systemLayoutSizeFittingSize的方法获取到cell的高。具体代码如下


@interface DataModel : NSObject 
@property (copy, nonatomic) NSString *text; 
@property (assign, nonatomic) CGFloat cellHeight; //缓存高度 
@end 
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath
*)indexPath { 
static CustomCell *cell; 
//只初始化一次cell 
static dispatch_once_t onceToken; 
dispatch_once(&onceToken, ^{ 
cell = [tableView
dequeueReusableCellWithIdentifier:NSStringFromClass([CustomCell class])];


Auto Layout动画


动画的话。因为布局约束就是要脱离frame这种表达方式的,可是动画是需要根据这个来执行,

这里面就会有些矛盾,不过根据前面说到的布局约束的原理,在某个时刻约束也是会被还原成

frame使视图显示,这个时刻可以通过layoutIfNeeded这个方法来进行控制。具体代码如下


make.top.bottom.left.right.equalTo(self.view).offset(10);
}];
[aniView mas_updateConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(self.view).offset(30);
}];
[UIView animateWithDuration:3 animations:^{
    [self.view layoutIfNeeded];
}];




目录
相关文章
|
3月前
解决Flutter报错boxconstraints has non-normalized height/width constraints
解决Flutter报错boxconstraints has non-normalized height/width constraints
41 0
|
XML Android开发 数据格式
View默认的LayoutParams是何时生成的,默认值是什么。layout_width和layout_height属性在哪里生效
View默认的LayoutParams是何时生成的,默认值是什么。layout_width和layout_height属性在哪里生效
|
容器 前端开发
|
Android开发 数据格式 XML
为什么relativelayout.layoutParams的width为-1
源码里看下就知道了。。 -1不代表宽度,代表MATCH_PARENT常量的值public static final int FILL_PARENT = -1;public static final int MATCH_PARENT = -1;public static final int WRA...
992 0