Autolayout

屏幕适配Autoresizing

屏幕适配的发展简史

  • iPhone3GS\iPhone4
    • 没有屏幕适配可言
    • 全部用frame、bounds、center进行布局
    • 很多这样的现象:坐标值、宽度高度值全部写死
1
2
UIButton *btn1 = [[UIButton alloc] init];
btn1.frame = CGRectMake(0, 0, 320 - b, 480 - c);
  • iPad出现、iPhone横屏

    • 出现Autoresizing技术
      • 让横竖屏适配相对简单
      • 让子控件可以跟随父控件的行为自动发生相应的变化
      • 前提是:关闭Autolayout功能
      • 局限性
        • 只能解决子控件跟父控件的相对关系问题
        • 不能解决兄弟控件的相对关系问题
  • iOS 6.0(Xcode4)开始

    • 出现了Autolayout技术
    • 从Xcode5.0(iOS 7.0)开始,开始流行Autolayout

设备分辨率

Autoresizing代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#import "ViewController.h"

@interface ViewController ()
/** 蓝色 */
@property (nonatomic, strong) UIView *blueView;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
blueView.frame = CGRectMake(0, 0, 250, 250);
[self.view addSubview:blueView];
self.blueView = blueView;

UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
redView.frame = CGRectMake(0, 150, 250, 100);
redView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
[blueView addSubview:redView];
}

// 点击屏幕会被调用的方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGFloat w = 200 + arc4random_uniform(100);
CGFloat h = 200 + arc4random_uniform(100);
self.blueView.frame = CGRectMake(0, 0, w, h);
}
@end

UIView中autoresizingMask属性值的含义:

1
2
3
4
5
6
7
8
9
/**
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0, 距离父控件左边的间距是伸缩的(不固定的)
UIViewAutoresizingFlexibleRightMargin = 1 << 2, 距离父控件右边的间距是伸缩的(不固定的)
UIViewAutoresizingFlexibleTopMargin = 1 << 3, 距离父控件顶部的间距是伸缩的(不固定的)
UIViewAutoresizingFlexibleBottomMargin = 1 << 5, 距离父控件底部的间距是伸缩的(不固定的)
UIViewAutoresizingFlexibleWidth = 1 << 1, 宽度跟随父控件的宽度进行自动伸缩
UIViewAutoresizingFlexibleHeight = 1 << 4, 高度跟随父控件的高度进行自动伸缩
*/

屏幕适配Autolayout

  • 苹果官方也推荐开发者尽量使用Autolayout来布局UI界面

  • Autolayout能很轻松地解决屏幕适配的问题

Autolayout中的错误和警告

  1. 警告(黄色)
    控件的frame不匹配所添加的约束, 比如
    比如约束控件的宽度为100, 而控件现在的宽度是110

  2. 错误(红色)
    缺乏必要的约束, 比如
    只约束了宽度和高度, 没有约束具体的位置

  3. 两个约束冲突, 比如
    1个约束控件的宽度为100, 1个约束控件的宽度为110

Autolayout代码实现

  • 代码实现Autolayout的步骤
    1. 利用NSLayoutConstraint类创建具体的约束对象
    2. 添加约束对象到相应的view上
1
2
- (void)addConstraint:(NSLayoutConstraint *)constraint;
- (void)addConstraints:(NSArray *)constraints;

代码实现Autolayout的注意点

  1. 要先禁止autoresizing功能,设置view的下面属性为NO
1
2
3
4
5
6
7
/* by default, the autoresizing mask on a view gives rise to constraints
that fully determine the view's position.
Any constraints you set on the view are likely to conflict with autoresizing constraints,
so you must turn off this property first.
IB will turn it off for you.
*/
view.translatesAutoresizingMaskIntoConstraints = NO;
  • 添加约束之前,一定要保证相关控件都已经在各自的父控件上
  • 一个NSLayoutConstraint对象就代表一个约束

** 约束要添加到约束所关联的父控件上**

创建约束对象的常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
+(id)constraintWithItem:(id)view1
attribute:(NSLayoutAttribute)attr1
relatedBy:(NSLayoutRelation)relation
toItem:(id)view2
attribute:(NSLayoutAttribute)attr2
multiplier:(CGFloat)multiplier
constant:(CGFloat)c;

*view1 :要约束的控件
*attr1 :约束的类型(做怎样的约束)
*relation :与参照控件之间的关系
*view2 :参照的控件
*attr2 :约束的类型(做怎样的约束)
*multiplier :乘数
*c :常量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
- (void)viewDidLoad {
[super viewDidLoad];


UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
// 不要将AutoresizingMask转为Autolayout的约束
blueView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:blueView];

UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
// 不要将AutoresizingMask转为Autolayout的约束
redView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:redView];// 添加宽度约束:100

/************************** 蓝色 **************************/
// 添加高度约束:40
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:40];
[blueView addConstraint:heightConstraint];

// 添加左边约束:blueView的左边距离父控件左边有20的间距
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:20];
[self.view addConstraint:leftConstraint];

// 添加右边约束:blueView的右边距离父控件右边有20的间距
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:-20];
[self.view addConstraint:rightConstraint];

// 添加顶部约束:blueView的顶部距离父控件顶部有20的间距
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:20];
[self.view addConstraint:topConstraint];

/************************** 红色 **************************/
// 添加高度约束:蓝色等高
NSLayoutConstraint *heightConstraint2 = [NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:blueView attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0];
[self.view addConstraint:heightConstraint2];

// 添加左边约束:redView的左边 == 父控件的中心x
NSLayoutConstraint *leftConstraint2 = [NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0];
[self.view addConstraint:leftConstraint2];

// 添加顶部约束:redView的顶部距离blueView的底部有20的间距
NSLayoutConstraint *topConstraint2 = [NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:blueView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:20];
[self.view addConstraint:topConstraint2];

// 添加右边约束:redView的右边 == blueView的右边
NSLayoutConstraint *rightConstraint2 = [NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:blueView attribute:NSLayoutAttributeRight multiplier:1.0 constant:0];
[self.view addConstraint:rightConstraint2];
}

/**
* 宽度高度为100,在父控件中垂直水平居中
*/
- (void)test2
{
UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
// 不要将AutoresizingMask转为Autolayout的约束
blueView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:blueView];

// 添加宽度约束:父控件的一半
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:0.5 constant:0];
[self.view addConstraint:widthConstraint];

// 添加高度约束:父控件的一半
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:0.5 constant:0];
[self.view addConstraint:heightConstraint];

// 水平居中
NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0];
[self.view addConstraint:centerXConstraint];

// 垂直居中
NSLayoutConstraint *centerYConstraint = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0];
[self.view addConstraint:centerYConstraint];
}

/**
* 宽度高度为100,粘着父控件的右上角
*/
- (void)test1
{
UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
// 不要将AutoresizingMask转为Autolayout的约束
blueView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:blueView];

// 添加宽度约束:100
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:100];
[blueView addConstraint:widthConstraint];

// 添加高度约束:100
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:100];
[blueView addConstraint:heightConstraint];

// 添加右边约束:blueView的右边距离父控件右边有10的间距
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:-10];
[self.view addConstraint:rightConstraint];

// 添加顶部约束:blueView的顶部距离父控件顶部有10的间距
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:10];
[self.view addConstraint:topConstraint];
}

@end

自动布局的核心计算公式

  • 自动布局的核心计算公式

obj1.property1 =(obj2.property2 * multiplier)+ constant value

VFL语言

  • VFL全称是Visual Format Language,翻译过来是“可视化格式语言”

  • VFL是苹果公司为了简化Autolayout的编码而推出的抽象语言

VFL语言实例和代码实现

  1. H:[cancelButton(72)]-12-[acceptButton(50)]

    • canelButton宽72,acceptButton宽50,它们之间间距12
  2. H:[wideView(>=60@700)]

    • wideView宽度大于等于60point,该约束条件优先级为700(优先级最大值为1000,优先级越高的约束越先被满足)
  3. V:[redBox][yellowBox(==redBox)]

    • 竖直方向上,先有一个redBox,其下方紧接一个高度等于redBox高度的yellowBox
  4. H:|-10-[Find]-[FindNext]-[FindField(>=20)]-|

    • 水平方向上,Find距离父view左边缘默认间隔宽度,之后是FindNext距离Find间隔默认宽度;再之后是宽度不小于20的FindField,它和FindNext以及父view右边缘的间距都是默认宽度。(竖线“|” 表示superview的边缘)

使用VFL来创建约束数组

1
2
3
4
5
6
7
8
9
+ (NSArray *)constraintsWithVisualFormat:(NSString *)
format options:(NSLayoutFormatOptions)opts
metrics:(NSDictionary *)metrics

views:(NSDictionary *)views;
*format :VFL语句
*opts :约束类型(顶部对齐 底部对齐等等)
*metrics :VFL语句中用到的具体数值
*views :VFL语句中用到的控件

创建一个字典(内部包含VFL语句中用到的控件)的快捷宏定义

1
NSDictionaryOfVariableBindings(...)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
// 不要将AutoresizingMask转为Autolayout的约束
blueView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:blueView];

UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
// 不要将AutoresizingMask转为Autolayout的约束
redView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:redView];

// 间距
NSNumber *margin = @20;

// 添加水平方向的约束
NSString *vfl = @"H:|-margin-[blueView]-margin-[redView(==blueView)]-margin-|";
NSDictionary *views = NSDictionaryOfVariableBindings(blueView, redView);
NSDictionary *mertrics = NSDictionaryOfVariableBindings(margin);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:vfl options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom metrics:mertrics views:views];
[self.view addConstraints:constraints];

// 添加竖直方向的间距
NSNumber *height = @40;
NSString *vfl2 = @"V:[blueView(height)]-margin-|";
NSDictionary *mertrics2 = NSDictionaryOfVariableBindings(margin, height);
NSArray *constraints2 = [NSLayoutConstraint constraintsWithVisualFormat:vfl2 options:kNilOptions metrics:mertrics2 views:views];
[self.view addConstraints:constraints2];

}

- (void)test2
{
UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
// 不要将AutoresizingMask转为Autolayout的约束
blueView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:blueView];

// 间距
NSNumber *margin = @20;

// 添加水平方向的约束
NSString *vfl = @"H:|-margin-[blueView]-margin-|";
NSDictionary *views = NSDictionaryOfVariableBindings(blueView);
NSDictionary *mertrics = NSDictionaryOfVariableBindings(margin);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:vfl options:kNilOptions metrics:mertrics views:views];
[self.view addConstraints:constraints];

// 添加竖直方向的间距
NSNumber *height = @40;
NSString *vfl2 = @"V:|-margin-[blueView(height)]";
NSDictionary *mertrics2 = NSDictionaryOfVariableBindings(margin, height);
NSArray *constraints2 = [NSLayoutConstraint constraintsWithVisualFormat:vfl2 options:kNilOptions metrics:mertrics2 views:views];
[self.view addConstraints:constraints2];
}

- (void)test1
{
UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
// 不要将AutoresizingMask转为Autolayout的约束
blueView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:blueView];

// 添加水平方向的约束
NSString *vfl = @"H:|-20-[blueView]-20-|";
NSDictionary *views = @{@"blueView" : blueView};
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:vfl options:kNilOptions metrics:nil views:views];
[self.view addConstraints:constraints];

// 添加竖直方向的间距
NSString *vfl2 = @"V:|-20-[blueView(40)]";
NSDictionary *views2 = @{@"blueView" : blueView};
NSArray *constraints2 = [NSLayoutConstraint constraintsWithVisualFormat:vfl2 options:kNilOptions metrics:nil views:views2];
[self.view addConstraints:constraints2];
}
@end

VFL语言不能实现的

Masonry

  • 目前最流行的Autolayout第三方框架
  • 用优雅的代码方式编写Autolayout
  • 省去了苹果官方恶心的Autolayout代码
  • 大大提高了开发效率

点击此处获取框架地址

  • 添加2个宏、导入主头文件
1
2
3
4
5
6
// 只要添加了这个宏,就不用带mas_前缀
#define MAS_SHORTHAND
// 只要添加了这个宏,equalTo就等价于mas_equalTo
#define MAS_SHORTHAND_GLOBALS
// 这个头文件一定要放在上面两个宏的后面
#import "Masonry.h"
  • 添加约束的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 这个方法只会添加新的约束
[view makeConstraints:^(MASConstraintMaker *make) {

}];

// 这个方法会将以前的所有约束删掉,添加新的约束
[view remakeConstraints:^(MASConstraintMaker *make) {

}];

// 这个方法将会覆盖以前的某些特定的约束
[view updateConstraints:^(MASConstraintMaker *make) {

}];
  • 约束的类型
1
2
3
4
1.尺寸:width\height\size
2.边界:left\leading\right\trailing\top\bottom
3.中心点:center\centerX\centerY
4.边界:edges
文章作者: Ammar
文章链接: http://lizhaoloveit.cn/2014/09/20/Autolayout/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ammar's Blog
打赏
  • 微信
  • 支付宝

评论