常规的基于frame的布局只能满足简单布局需求,无法适应横竖屏,多分辨率的场景。AutoLayout是一种基于描述性的语言,和frame的区别是:frame写的是位置,AutoLayout写的是约束条件,系统根据约束条件生成frame。
- NSLayoutConstraint
- VFL
NSLayoutConstraint
使用NSLayoutConstraint传入一系列约束,系统就能自动完成frame的计算和布局:
约束的语法:
item1.attribute1 = multiplier × item2.attribute2 + constant // 倍数 * 锚定元素位置 + 偏移量 // 例如: button2.leading = 1.0 × button1.trailing + 8.0
NSLayoutRelation设置≥ = ≤:
typedef NS_ENUM(NSInteger, NSLayoutRelation) { NSLayoutRelationLessThanOrEqual = -1, NSLayoutRelationEqual = 0, NSLayoutRelationGreaterThanOrEqual = 1, };
NSLayoutAttribute设置位置属性,看名字就知道啥意思:
typedef NS_ENUM(NSInteger, NSLayoutAttribute) { NSLayoutAttributeLeft = 1, NSLayoutAttributeRight, NSLayoutAttributeTop, NSLayoutAttributeBottom, NSLayoutAttributeLeading, NSLayoutAttributeTrailing, NSLayoutAttributeWidth, NSLayoutAttributeHeight, NSLayoutAttributeCenterX, NSLayoutAttributeCenterY, NSLayoutAttributeLastBaseline, NSLayoutAttributeBaseline NS_SWIFT_UNAVAILABLE("Use 'lastBaseline' instead") = NSLayoutAttributeLastBaseline, NSLayoutAttributeFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeNotAnAttribute = 0 };
例如固定宽高为30的图片,在父view垂直居中,距离父view左边距15:
[NSLayoutConstraint activateConstraints:@[ // 头像的竖向布局约束:它的中心距离它父view的中心是:1倍+0,即垂直居中 [NSLayoutConstraint constraintWithItem:_avatorImageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0], // 头像的横向布局约束:它的左边距距离它的父view的左边距是:1倍+15 [NSLayoutConstraint constraintWithItem:_avatorImageView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:15], // 头像的宽度不依赖其他view,固定为30 [NSLayoutConstraint constraintWithItem:_avatorImageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:30], [NSLayoutConstraint constraintWithItem:_avatorImageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:30], // 名称相对于头像定位,名称的垂直中间等于头像的垂直中间 [NSLayoutConstraint constraintWithItem:_nickLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_avatorImageView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0], // 名称相对于头像定位,名称的左边距离头像的右边距是:1倍+0 [NSLayoutConstraint constraintWithItem:_nickLabel attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:_avatorImageView attribute:NSLayoutAttributeRight multiplier:1 constant:0], ]];
VFL
上面一个简单的图片加文字的布局写起约束来还是比较麻烦的。系统还提供了更便捷的VFL(Visual Format Language)这种 DSL(Domain Specific Language)领域特定语言,能让约束条件写起来更简单点。
可以写一段:
NSString *vflString = @"H:|-15-[_view1]-0-[_Label1]-(>=0)-[_view2(==_view1)]-0-[_Label2]-15-[_view3(==_view1)]-0-[_Label3]-15-[_view4(==_view1)]-0-[_Label4]-15-|"; [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vflString options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(_view1, _Label1, _view2, _Label2, _view3, _Label3, _view4, _Label4) ]];
总结
AutoLayout的缺点是有一定学习成本,在如滚动等性能敏感的场景下表现不如frame布局(但IOS12苹果地利用了Cassowary算法的高效的界面线性更新策略,大大提升了AutoLayout的性能)。不过我们要向前看,用github上开源的库,看起来很好用,但特别依赖IOS版本,经常出现版本升级后,库就不维护的情况,反之用系统提供的方案能一直受益于苹果工程师们的优化迭代。