Auto Layout总结

Auto Layout 小总结。

Auto Layout 介绍

Auto Layout的由来

2011年初次出现在OS X 10.7中,初次出现在iOS是在2012年的iOS6.旨在取代基于springstrutAutosizing.

Auto Layout 的前身是 Cassowary.

使用Auto Layout 的好处

  1. 几何关系。
  2. 内容驱动的布局。
  3. 优先级规则。
  4. 检查和模块化。
  5. Autosizing 兼容。

约束

1.可满足性。(不得有冲突)
2.充分性。 (信息足以确定大小和位置)

约束属性

属性:

Auto Layout Attributes Notes
Height
Width
Top 上侧
Bottom 下侧
BaseLine 文本基线
Leading 前边
Trailing 后边
Left
Right
CenterX 横向中点
CenterY 纵向中点

在从左到右的语言环境中 LeadingLeftTrailingRight相等,从右到左的语言环境中(如阿拉伯语,希伯来语),则会对调。

关系:

NSLayoutRelation Notes
LessThanOrEqual 小于或者等于
Equal 等于
GreaterThanOrEqual 大于或者等于

关于那些丢失的视图

1
@property(nonatomic) BOOL translatesAutoresizingMaskIntoConstraints

By default, the property is set to YES for any view you programmatically create. If you add views in Interface Builder, the system automatically sets this property to NO.
–Apple 的文档写到,使用纯代码时,该属性默认为 YES,
当使用 Interface Builder 时,系统将自动将它设为NO.
使用Auto Layout应该先把该属性设为 NO ,否则View还是会按照以往的 AutoresizingMask 进行计算.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker install];
}

- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
constraintMaker.updateExisting = YES;
block(constraintMaker);
return [constraintMaker install];
}

- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
constraintMaker.removeExisting = YES;
block(constraintMaker);
return [constraintMaker install];
}

在著名的自动布局框架Masonry中,可以看到,每次更改约束,都会先self.translatesAutoresizingMaskIntoConstraints = NO;;

有歧义的布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import "UIView+Test.h"

@implementation UIView (Test)

-(void)jat_testAmbiguity{
NSLog(@"<%@:0x%0x>:%@",
self.class.description,(int)self,
self.hasAmbiguousLayout?@"有歧义":@"没有歧义");
for (UIView *view in self.subviews) {
[view Jatstar_testAmbiguity];
}
// hasAmbiguousLayout: 检查约束是否有歧义
}
@end

内在内容的大小

1
- (CGSize)intrinsicContentSize

视图的本身大小,根据视图的内容决定,如Label的文字,ImageView 的图片。
当视图存在内在大小时,可以只设置视图的位置,它将和内在大小共同构成无歧义的布局。

压缩阻力和内在吸附

Auto Layout 遇到两个相互矛盾的请求时,只会让优先级更高的起作用。

  1. 压缩阻力:视图压缩的能力。默认值750;
  2. 内容吸附:视图抵抗拉伸的能力。默认值250.

水平轴和垂直轴分别设置。取值范围都是[1,1000]

1
2
3
4
5
// 设置视图抵抗压缩的优先级。
- (void)setContentCompressionResistancePriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis

// 设置视图抵抗拉伸的优先级。
- (void)setContentHuggingPriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis

图像装饰元素

使用Auto Layout 布局 UIImageView 时,自动布局无法对图片中的硬编码装饰元素(如:高光,阴影)作出调整,你必须自己指出。

1
UIImage *insetImage = [shadowedImage imageWithAlignmentRectInsets:UIEdgeInsetsMake(0, 0, 20, 20)];

UIEdgeInsets 构造器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
计算 inset

@param alignmentRect (不包括阴影)
@param imageBounds (包括阴影)

@return intset
*/
UIKIT_STATIC_INLINE UIEdgeInsets BuildInset(CGRect alignmentRect ,CGRect imageBounds){

CGRect targetRect = CGRectIntersection(alignmentRect, imageBounds);

UIEdgeInsets inset;
inset.left = CGRectGetMidX(targetRect) - CGRectGetMidX(imageBounds);
inset.right = CGRectGetMaxX(imageBounds) - CGRectGetMaxX(targetRect);
inset.top = CGRectGetMidY(targetRect) - CGRectGetMidY(imageBounds);
inset.bottom = CGRectGetMaxY(imageBounds) - CGRectGetMaxY(targetRect);
return inset;
}

约束

约束类型

  • NSLayoutContraint :公有,布局约束。用于设置view在view tree之间的关系,自身大小等。
  • NSContentSizeLayourConstraint :私有,内容大小约束。主要包含 内容吸附 和内容压缩。
  • NSAutoresizingMaskLayoutConstrin :私有,由 AutosizingMask 转换到 AutoLayout系统中的约束表达。
  • _UILayoutSupportConstraint :私有,iOS7中新增,包括 topLayoutGuidebottomLayoutGuide ,用于防止视图与状态栏之类的障碍物重合。
  • NSIBPrototypingLayoutConstraint:“私有,iOS7新增,如果在 Interface Builder 中添加控件,并且没有在StoryBoard中为该控件设置约束,但是标注了使用AutoLayout的话,在运行时期,系统会默认为该控件添加该约束。
1
2
3
4
5
6
7
8
9
// 避免 navigationBar 和 tabBar 的遮挡
[testView mas_makeConstraints:^(MASConstraintMaker *make) {
UIView *topLayoutGuide = (id)self.topLayoutGuide;
make.top.equalTo(topLayoutGuide.mas_bottom);
UIView *bottomLayoutGuide = (id)self.bottomLayoutGuide;
make.bottom.equalTo(bottomLayoutGuide.mas_top);
make.left.equalTo(self.view);
make.right.equalTo(self.view);
}];

约束的优先级

衡量约束重要性的指标。 同一约束条件 AutoLayout 会使优先级更高的约束生效。
优先级是浮点数,但通常用其整数表达。

线性公式

1
"view1.attr1 = view2.attr2 * multiplier + constant"

这里可以是 == <= >=;

构建约束的方式

  • Interface Builder.
  • Visual Format Language(VFL).
  • NSLayoutConstraint实例

UIScrollView And Autolayout

UIScrollView依靠与其subviews之间的约束来确定ContentSize的大小

1
2
3
4
5
6
7
8
9
10
UIView *container = [UIView new];
[scrollView addSubview:container];
[container mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(scrollView);
make.width.equalTo(scrollView);
make.height.equalTo(scrollView).multipliedBy(2.0);
}];

//ContentSize的width等于scrollView的width
//ContentSize的height等于scrollView的height的两倍

相关链接

UIScrollView And Autolayout
iOS Auto Layout Demystified
GitHub - SnapKit/Masonry
Masonry介绍与使用实践
UIScrollview与Autolayout的那点事