布局:AutoLayout,VFL

22 9月

常规的基于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版本,经常出现版本升级后,库就不维护的情况,反之用系统提供的方案能一直受益于苹果工程师们的优化迭代。

发表评论

电子邮件地址不会被公开。 必填项已用*标注