如何优雅的代码编写 AutoLayout
Posted 小荷才露尖尖角1213
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何优雅的代码编写 AutoLayout相关的知识,希望对你有一定的参考价值。
概述
使用 Objective-C 纯代码编写 AutoLayout,看 AutoLayout 的字面理解就是自动布局,听起来好像蛮屌的样子。说白了就是适配:适应、兼容各种不同的情况,包括不同版本的操作系统的适配(系统适配)和不同屏幕尺寸的适配(屏幕适配)。
在 Storyboard 中,AutoLayout 有以下 3 个常用面板:
-
Align(对齐)
-
Pin(相对)
-
Resolve Auto Layout Issues(约束处理)
在 Storyboard 中实现 AutoLayout 我就不在本文讲解,因为讲了就是违背了不忘初心,方得始终的标题了。
Talk is cheap, show me the code
先说一下用代码实现 AutoLayout 步骤,别眨眼:
-
利用
NSLayoutConstraint
类创建具体的约束对象; -
添加约束对象到相应的 view 上,代码有这两种:
- (void)addConstraint:(NSLayoutConstraint *)constraint;
- (void)addConstraints:(NSArray *)constraints;
或许有人问了,原来才两个步骤就可以了,我刚刚裤子都脱了,你就给我看这个?!
话不多说,马上 show you the code !
先看看我们使用 frame 的方式是如何确定一个 view 的位置的:
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"使用 frame 的方式";
UIView *purpleView = [[UIView alloc] initWithFrame:CGRectMake(100, 200, 150, 150)];
purpleView.backgroundColor = [UIColor purpleColor];
[self.view addSubview:purpleView];
}
代码很简单,运行效果如下:
再来看看 AutoLayout 的实现:
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"使用 AutoLayout 的方式";
UIView *purpleView = [[UIView alloc] init];
purpleView.backgroundColor = [UIColor purpleColor];
// 禁止将 AutoresizingMask 转换为 Constraints
purpleView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:purpleView];
// 添加 width 约束
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:purpleView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:150];
[purpleView addConstraint:widthConstraint];
// 添加 height 约束
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:purpleView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:150];
[purpleView addConstraint:heightConstraint];
// 添加 left 约束
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:purpleView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:100];
[self.view addConstraint:leftConstraint];
// 添加 top 约束
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:purpleView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:200];
[self.view addConstraint:topConstraint];
}
看完这段代码,我收到了惊吓!我被这一大段代码吓到了,很多童鞋看到那么简单的布局需要写那么多代码,可能就被吓跑了。我只能说一句:先不要走,待我慢慢解释~
- 创建约束对象(NSLayoutConstraint)的常用方法
一个NSLayoutConstraint
对象就代表一个约束。
+ (id)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;
总共有 7 个参数 ??,那就以 leftConstraint 为例吧介绍这 7 个参数吧
? view1: 要约束的控件(purpleView)
? attr1: 约束的类型(常量),就是要做怎么样的约束,大家可以进去看看都有什么常量(这里是NSLayoutAttributeLeft)
? relation: 与参照控件之间的关系(常量),包括等于、大于等于、小于等于(NSLayoutRelationEqual 是指等于)
? view2: 参照的控件(self.view)
? attr2: 约束的类型(常量),就是要做怎么样的约束,大家可以进去看看都有什么常量(这里是NSLayoutAttributeLeft)(NSLayoutAttributeLeft)
? multiplier: 乘数,就是多少倍(1.0)
? c: 常量,做好了上述的约束之后会加上这个常量(100)
所以 leftConstraint 就是代表:要约束的控件purpleView
的左间距
是等于
参照控件 self.view
的左间距
的 1.0 倍
加上 100
。
所以我们得出 AutoLayout 的核心计算公式:
obj1.property1 =(obj2.property2 * multiplier)+ constant value
- 添加约束(addConstraint)的规则
在创建约束了之后,需要将其添加到作用的控件上才能生效,注意在添加约束的时候目标控件需要遵循以下规则(这里控件就用 view 简单表示吧):
(1)对于两个同层级 view 之间的约束关系,添加到它们的父 view 上
(2)对于两个不同层级 view 之间的约束关系,添加到他们最近的共同父 view 上
(3)对于有层次关系的两个 view 之间的约束关系,添加到层次较高的父 view 上
(4)对于比如长宽之类的,只作用在该 view 自己身上的话,添加到该 view 自己上,不用图了吧??
可以看出,widthConstraint 和 Constraint 属于第(4)种,leftConstraint 和 rightConstraint 属于第(3)种。
- 代码实现 AutoLayout 的注意事项
如果只是创建和添加了约束,是不能正常运行的,要做好以下的工作:
(1)要先禁止 autoresizing 功能,防止 AutoresizingMask 转换成 Constraints,避免造成冲突,需要设置 view 的下面属性为 NO:
(2)添加约束之前,一定要保证相关控件都已经在各自的父控件上。用上面的例子就是 [self.view addSubview:purpleView]; 一定要放在添加 left 约束之前,否则程序会 crash,因为要确保 purpleView 要已经在 self.view 上了。建议先写 [self.view addSubview:purpleView]; 之后,再专心写约束。view.translatesAutoresizingMaskIntoConstraints = NO;
(3)不用再给 view 设置 frame
看到了吧,那么简单的一个界面,用 AutoLayout 实现的话竟然要那么多代码,感觉上并没有那么方便是吧?
其实 AutoLayout 要看应用内容决定,上面只是一个使用的 demo。如果你的内容是信息众多,同时需要展示的类别也很多,尺寸动态不定,比如说微博列表、QQ 动态列表等等,写这些复杂界面使用 AutoLayout 能给予(jǐ yǔ??)很大的帮助。
Apple 为了简化 AutoLayout 复杂的代码,开发了一种 VFL 语言(Visual format language),事实上没看见简化多少,而且还有比较大的局限性,这里就不介绍了,想了解的童鞋自己 Google 去。
如何优雅的代码编写 AutoLayout
看到了 Apple 自带的 AutoLayout 实现方式,感觉实在是太恶心了,那么如何优雅的代码编写 AutoLayout 呢?
—— 使用第三方框架 Masonry。
GitHub: https://github.com/SnapKit/Masonry
看它的介绍,感觉听牛掰的:Harness the power of AutoLayout NSLayoutConstraints with a simplified, chainable and expressive syntax. Supports ios and OSX Auto Layout.
看完 README.md 文件发现的确蛮优雅的。
先一览 Masonry 是如何实现 AutoLayout 的:
#import "ViewController.h"
#import "Masonry.h" // 第三方或自己写的用引号,系统自带用双引号。
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIView *purpleView = [[UIView alloc] init];
purpleView.backgroundColor = [UIColor purpleColor];
[self.view addSubview:purpleView];
[purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
// 在这个 block 里面,利用 make 对象创建约束
make.size.mas_equalTo(CGSizeMake(100, 100));
make.center.mas_equalTo(self.view);
}];
}
运行效果:
注意:purpleView.translatesAutoresizingMaskIntoConstraints = NO;
不需要在这里写了,因为 Masonry 已经写好了。
Masonry 开车,赶紧上车
一步一步跟着来,哈哈嘻嘻
// 长宽均为 100,粘着父 view 右下角
[purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(@100);
make.height.equalTo(@100);
make.right.equalTo(self.view);
make.bottom.equalTo(self.view);
}];
// 长宽均为 100,粘着父 view 右下角,间距为 16
[purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(@100);
make.height.equalTo(@100);
// 这里也可以写 make.right.equalTo(self.view.mas_right).offset(-16);
// 为了增强可读性,可以在 .offset 前加上 .with 或者 .and: make.right.equalTo(self.view).with.offset(-16); 看自己习惯吧
make.right.equalTo(self.view).offset(-16);
// 这里也可以写 make.right.equalTo(self.view.mas_bottom).offset(-16);
make.bottom.equalTo(self.view).offset(-16);
}];
看到上面代码的包装好的 @100,其实也可以直接传值 100,不过要把 equalTo
改成 mas_equalTo
,这样它就自动帮你包装好了。
make.width.mas_equalTo(100);
make.height.mas_equalTo(100);
其实 mas_equalTo
就是一个宏,大家可以进去看看定义。
mas_equalTo
这个方法会对参数进行包装equalTo
这个方法不会对参数进行包装mas_equalTo
的功能强于equalTo
大家可能会觉得有点儿晕,有时候用 mas_equalTo
,有时候用 equalTo
,其实大家可以在 pch 文件里定义两个宏,就可以完美解决这个纠结问题。注意要写在 #import "Masonry.h"
前面。
//define this constant if you want to use Masonry without the ‘mas_‘ prefix,这样子 `mas_width` 等就可以写成 `width`
#define MAS_SHORTHAND
//define this constant if you want to enable auto-boxing for default syntax,这样子 `mas_equalTo` 和 `equalTo` 就没有区别了
#define MAS_SHORTHAND_GLOBALS
好,现在来一个稍微比刚才的复杂一点点的界面:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *purpleView = [[UIView alloc] init];
purpleView.backgroundColor = [UIColor purpleColor];
[self.view addSubview:purpleView];
UIView *orangeView = [[UIView alloc] init];
orangeView.backgroundColor = [UIColor orangeColor];
[self.view addSubview:orangeView];
CGFloat margin = 16;
CGFloat height = 32;
[purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(margin);
make.bottom.equalTo(self.view).offset(-margin);
make.right.equalTo(orangeView.left).offset(-margin);
make.height.equalTo(height);
make.width.equalTo(orangeView);
}];
[orangeView mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(self.view).offset(-margin);
make.right.equalTo(self.view).offset(-margin);
make.height.equalTo(height);
}];
}
其实实现这个界面有很多中写法,大家可以试试,比如说这样写:
- (void)viewDidLoad {
...
[purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(margin);
make.bottom.equalTo(self.view).offset(-margin);
make.right.equalTo(orangeView.left).offset(-margin);
make.height.equalTo(height);
make.height.equalTo(orangeView);
make.width.equalTo(orangeView);
make.top.equalTo(orangeView);
}];
[orangeView mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.view).offset(-margin);
}];
}
原文链接:http://www.jianshu.com/p/3c7e202c76b2
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
以上是关于如何优雅的代码编写 AutoLayout的主要内容,如果未能解决你的问题,请参考以下文章
如何编写优雅的异步代码 — CompletableFuture
在编写RTOS代码时,如何设计一个简单优雅可拓展的任务初始化结构?
在编写RTOS代码时,如何设计一个简单优雅可拓展的任务初始化结构?
QMT策略编写如何优雅地调教QMT量化平台编写量化策略(使用notepad++pycharmvscode等外部IDE编写量化交易代码)