史上比较用心的纯代码实现 AutoLayout

入职有两三个月了吧,都是使用 Objective-C 纯代码(虽然有时候偷偷参杂一些 Swift 开源库)来编写公司APP,写布局的时候几乎都是要么在初始化的时候用 initWithFrame,要么就初始化完毕之后用 view.frame。虽然这种方法很直观,一眼就可以看出这个 view 的位置以及大小,但是坏处也是有的,比如说在计算的时候麻烦等等。

概述

使用 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 上,代码有这两种:


1

2

- (void)addConstraint:(NSLayoutConstraint *)constraint;

- (void)addConstraints:(NSArray *)constraints;

或许有人问了,原来才两个步骤就可以了,我刚刚裤子都脱了,你就给我看这个?!

话不多说,马上 show you the code !

先看看我们使用 frame 的方式是如何确定一个 view 的位置的:


1

2

3

4

5

6

7

- (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 的实现:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

- (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 对象就代表一个约束。


1

+ (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 的核心计算公式:


1

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:


1

view.translatesAutoresizingMaskIntoConstraints = NO;

(2) 添加约束之前,一定要保证相关控件都已经在各自的父控件上。用上面的例子就是 [self.view addSubview:purpleView]; 一定要放在添加 left 约束之前,否则程序会 crash,因为要确保 purpleView 要已经在 self.view 上了。建议先写 [self.view addSubview:purpleView]; 之后,再专心写约束。

(3)不用再给 view 设置 frame

看到了吧,那么简单的一个界面,用 AutoLayout 实现的话竟然要那么多代码,感觉上并没有那么方便是吧?

其实 AutoLayout 要看应用内容决定,上面只是一个使用的 demo。如果你的内容是信息众多,同时需要展示的类别也很多,尺寸动态不定,比如说微博列表、QQ 动态列表等等,写这些复杂界面使用 AutoLayout 能给予(jǐ yǔ??)很大的帮助。

Apple 为了简化 AutoLayout 复杂的代码,开发了一种 VFL 语言(Visual format language),事实上没看见简化多少,而且还有比较大的局限性,这里就不介绍了,想了解的童鞋自己 Google 去。

算了,给个官方链接吧:Visual Format Language

如何优雅的代码编写 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 的:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

#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);

    }];

}

运行效果:

创建一个长和宽均为 100、与父 view 居中的 view

注意:purpleView.translatesAutoresizingMaskIntoConstraints = NO;不需要在这里写了,因为 Masonry 已经写好了。

Masonry 开车,赶紧上车

一步一步跟着来,哈哈嘻嘻


1

2

3

4

5

6

7

// 长宽均为 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);

}];


1

2

3

4

5

6

7

8

9

10

// 长宽均为 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,这样它就自动帮你包装好了。


1

2

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" 前面。


1

2

3

4

//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

好,现在来一个稍微比刚才的复杂一点点的界面:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

- (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);

    }];

}

两个等高等宽的 view 平分屏幕宽度,带有间隙

其实实现这个界面有很多中写法,大家可以试试,比如说这样写:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

- (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);

    }];

}

总结

其实 Masonry 的文档已经很详细了,建议大家去看文档,我写这个主要是为了做这个界面的 Tableview 上下拉阻尼效果而准备的

对我粗暴~

本文作者:伯乐在线 - 小良 。

时间: 2024-11-10 16:12:58

史上比较用心的纯代码实现 AutoLayout的相关文章

iOS界面布局之三——纯代码的autoLayout及布局动画

iOS界面布局之三--纯代码的autoLayout及布局动画 一.引言 关于界面布局,apple的策略已经趋于成熟,autolayout的优势在开发中也已经展现的淋漓尽致.除了使用storyBoard进行布局约束的拖拽,有时我们也需要在代码中进行autolayout的布局设置,Masonry库可以方便的创建约束属性,实际上,我们也没有必要再使用系统原生的代码来创建和设置约束,这篇博客只作为使用的方法备忘.前几篇布局介绍的链接如下: 使用autoresizing进行界面布局:http://my.o

【iOS开发-113】在storyboard上用AutoLayout,纯代码实现AutoLayout布局方法以及简单动画

(1)在storyboard中使用AutoLayout.这个AutoLayout和autoResizing是冲突的,只能选其一. --不同级的控件的相互约束是添加在高层级上. --同级别的控件的相互约束是添加在它们的父控件上. --不同分支控件的相互约束是添加在它们向上追溯到的第一个共同父控件. 这3条规则在代码创建时有用.利用storyboard时系统自动帮我们添加好了. (2)用代码实现AutoLayout.步骤就是先创建布局约束对象,然后把这个对象添加到需要约束的控件中. --需要先禁止需

史上最用心的iOS App上架流程【转】

转:http://www.jianshu.com/p/16fa56eacb5e 题记 麻痹起来嗨!看网上那么多的教程,依然在我心爱的爱屁屁在上架的时候遇到各种 J8 问题,最大的问题就是:Xcode 证书什么的,Provisioning Profile 什么的,Debug 什么的,Production 和Distribution 什么乱 78 糟的都把我搞糊涂了,网上很多教程都是好旧的(虽然思路一样,但是不够详细),所以我打算把我今年已经上架的干货儿(下载地址,介绍地址)的上架艰辛历程写下来,一

【iOS开发】多屏尺的自动适配 AutoLayout (纯代码方式)

关于AutoLayout,最早从iOS6开始引入使用. 主要功能是使用约束,对视图进行相对布局,以适应不同屏尺的变换. 网上大量的资料都在介绍xib和storyboard,如何使用AutoLayout,说纯代码使用AutoLayout进行UI布局的越来越少.对于我这个习惯了代码UI布局的人,写个备忘: AutoLayout是什么? 使用一句Apple的官方定义的话 AutoLayout是一种基于约束的,描述性的布局系统. Auto Layout Is a Constraint-Based, De

纯代码添加约束,autolayout 适配

IOS AutoLayout 代码约束—VFL 2014-12-22 22:19:43http://my.oschina.net/carson6931-Carson6931-点击数:2148 IOS 提供了两种添加约束的方法 第一种: +(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(id)view2

纯代码Autolayout的三种方法

Autolayout讲解较多的就是xib和storyboard用法,本文主要记录纯代码的Autolayout使用方法: 方法1.苹果原生的方法,这种方法虽然简单但是太过繁杂,可用性很差 //宽度=superView高度 [superView addConstraint:[NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toIte

iOS纯代码自动布局的那些事(简介)

由于本人也是才开始学习自动布局,所以可能会出现错误,请大家多多包含,并指出错误 关于自动布局,现在很多介绍用xib和storyboard,但是纯代码使用Autolayout越来越少了 下面主要大概的介绍下这几天学习纯代码使用Autolayout的一些可能有用的东西吧. 首先先创建3个UIView 并对view进行初始化 在使用自动布局之前要对子视图的布局方式进行调整,用到 translatesAutoresizingMaskIntoConstraints这个UIView的属性,并且把它设置为no

如何优雅的代码编写 AutoLayout

概述 使用 Objective-C 纯代码编写 AutoLayout,看 AutoLayout 的字面理解就是自动布局,听起来好像蛮屌的样子.说白了就是适配:适应.兼容各种不同的情况,包括不同版本的操作系统的适配(系统适配)和不同屏幕尺寸的适配(屏幕适配).在 Storyboard 中,AutoLayout 有以下 3 个常用面板: Align(对齐) Align(对齐) Pin(相对) Pin(相对) Resolve Auto Layout Issues(约束处理) Resolve Auto

史上最牛逼的javascript俄罗斯方块,63行代码搞定啊

<!doctype html><html><head></head><body> <div id="box" style="width:252px;font:25px/25px 宋体;background:#000;color:#9f9;border:#999 20px ridge;text-shadow:2px 3px 1px #0f0;"></div> <script>