文档章节

AutoLayout(I):忘掉Frame,拥抱Constraint

hejunbinlan
 hejunbinlan
发布于 2016/07/29 14:29
字数 2645
阅读 16
收藏 0
点赞 0
评论 0

自从iPhone6和6plus出了之后,可以说iPhone进入到了大屏时代。在小屏的时代,常常有很多人是所谓的代码控,有的非常排斥IB这类做法,说什么效率问题。我从开始学习OC写UI,其实只写过4个多月的代码写UI,后来进入第一家公司,公司里面的UI全部是IB,从那时起,我就一直是IB写UI。我自己从来没有感觉到IB有什么不好的地方,效率也没有传说中那么多的问题。更为重要的是,用IB使我的工作效率大大提升,所见即所得。如今这个年代,如果你还坚持用代码写UI,那么恭喜你,你“too young”了,你有适配不完的屏幕。

苹果在iOS6的时候提供了autolayout-自动布局,在iOS8时候提供了class size。目前由于大多数的开发还是要兼容iOS6(iOS5真的没有必要去兼容了),所以在做适配的时候还是只能用autolayout,公司最近在做iPhone6的适配,之前的完全是用代码写死的,这次的适配也等于是基本上重做了。虽然之前简单的知道一些constraint,但是具体用到项目里面还是头一次,折腾了一些,也遇到了一些坑。

忘掉Frame,拥抱Constraint

AutoLayout的第一条信条就是忘记Frame,在传统的写View大小我们可以用Frame来做,但是在AutoLayout下,Frame已经不能满足我们的需求了,应当用constraint来确定View的大小。

raywenderlinch上有两篇写AutoLayout的文章:

http://www.raywenderlich.com/50317/beginning-auto-layout-tutorial-in-ios-7-part-1

http://www.raywenderlich.com/50317/beginning-auto-layout-tutorial-in-ios-7-part-2

感兴趣的同学可以进去看看。这篇blog,会以一个demo的形式铺开。

IB加约束

新建一个工程,命名为AutoLayoutDemo。
可以看到新建工程的目录结构是这样的。

点开main.stroyboard,可以看到在Xcode6后默认的view大小是600x600。首先这个不符合我们的审美,我们更希望它的尺寸和我们的手机屏幕一般,这样比较直观。我们点击viewController,在右侧的Simulated Metrics选项中选择size为iPhone4-inch(也可以根据自己的审美选择4.7inch或者其他的,最好在一个项目中能够统一)。

这个时候view就是4inch了。我们在注意Interface Builder Document这个栏目下,确保勾选中的Use Auto Layout选项,但是不能勾选Use Size Classes(因为要兼容iOS6和iOS7)。

初加约束

拖入一个UIView到ViewController.view上,设置width,height==200,backgroundColor为greenColor。与ViewController进行连线,并且命名为demoView。可以看到demoView的约束:

下面是约束的种类,列了一下并且做简单解释:

Leading Space to:Superview 相对父视图保持左对齐
Trailling Space to:Superview 相对父视图保持右对齐
Top Space to:SuperView 相对父视图顶部对齐
Bottom Space to:SupderView 相对父视图底部对齐
Width:自身约束宽
Height:自身约束高
Width Equally:view   和参考的view等宽
Height Equally:view  和参考的view等高
Baseline:view 和参考的view在同一水平线
Horizontal Space:view 和参考的view保持水平距离
Vertical Space:view 和参考的view保持垂直距离
Aspect Ratio: 保持宽高比例(暂时我还没有用到,不确定怎么用)

我们点中self.demoView,按住Ctrl键不动进行连线,可以注意到我们往不同的方向连线,系统提供的约束是有区别的。进行约束
完成约束:self.demoView在superView左对齐44像素,自身的宽高约束为200像素,距离superView的顶部92像素。这样的话self.demoView不能在何种大小的 屏幕下总是和superView左对齐44像素,上对齐92像素,自身宽高200 像素。

view之间的约束

constraint包含了Frame确定尺寸大小的功能但是又比Frame多了一个能够表述不同view之间的相互位置关系的功能。现在我们再建一个view,命名为libView。具体的需求是:让libView在垂直方向上和demoView对齐,并且libView的顶部始终与demoView保持30像素,libView的高度为40,宽度和demoView保持一致。

我们看到图中有黄色的线条,黄色的线条表示warning,红色的线表示约束错误,蓝色的表示约束正确。如果是红色的需要进行调整否则会可能会crash。
上图中遇到的黄色我们可以左侧的黄色按钮查看具体原因。这个时候我们只要将x和width调整为60和200就不会报warning了。跑一下,可以看看效果。

constraint也是可以成为property的

每一个constratint实际是NSLayoutConstraint的类,和其他的OC里面的类一样,也是继承自NSObject。上面的例子中我们把demoView的宽度约束为200,但是现在的需求是将宽度改为300。我们可以在脑海中想象一下这个布局,demoview的宽度会变为300,libView由于有Width Equal的约束,将会导致libView的宽度也会变成300。下面我们来具体实践一下:
将demoView的Width constraint连线到ViewControll.h里面作为一个property,并且命名为demoViewWidthConstraint。

打开ViewController.m文件,在ViewDidLoad里面:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.demoViewWidthConstraint.constant = 300;
}

run一下:

可以看到,demoView的宽度变成了300,并且libView也相应的变成了300,和之前的预期一样。

用代码写约束

IB的本质上是一种持久化的文件,它不具有继承等关系。我们可以这样理解,所有IB能够做的,代码一定可以做的。同样的,对于约束,我们可以用代码实现。
现在的需求是:建立一个view,让他距离self.view的顶部30像素,距离self.view的左边30,距离self.view的右边50像素,自身高度为40。

constraintWithItem写约束

NSLayoutConstraint提供了:

+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;

的方法,该方法的可以转化为一下计算公式:

view1.attr1 = view2.attr2 * multiplier + constant

可以翻译为:view1的某个约束是view2的某个约束乘以变量参数+约束值 得来的。

我们实际写出上面提出的需求,代码在ViewDidLoad里面如下:

//构造一个orangeColor的view
UIView *codeView = [[UIView alloc] init];
[self.view addSubview:codeView];
codeView.backgroundColor = [UIColor orangeColor];

codeView.translatesAutoresizingMaskIntoConstraints = NO;//防止与autosize冲突,一定要写,否则不能正常进行

//距离superView的顶部30像素 codeView的attributeTop约束等于superView的arttributeTop30像素
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:codeView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:codeView.superview attribute:NSLayoutAttributeTop multiplier:1 constant:30];
//距离superView的左边30
NSLayoutConstraint *leadConstraint = [NSLayoutConstraint constraintWithItem:codeView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:codeView.superview attribute:NSLayoutAttributeLeading multiplier:1 constant:30];
//距离superView的右边50像素
NSLayoutConstraint *trailConstraint = [NSLayoutConstraint constraintWithItem:codeView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:codeView.superview attribute:NSLayoutAttributeTrailing multiplier:1 constant:-50];
//自身高度为40
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:codeView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:40];
[self.view addConstraint:topConstraint];
[self.view addConstraint:leadConstraint];
[self.view addConstraint:trailConstraint];
[self.view addConstraint:heightConstraint];

run一下

效果符合我们的预期。

上面的代码要特别注意的就是translatesAutoresizingMaskIntoConstraints == NO,一定要这样设置,否则会于autoSize冲突导致约束失败。
另一方面我们也看到用这种方式写约束,代码量会瞬间加大不少。于是,聪明的苹果开发人员发明了一种新的语言——Visual format language(VFL)。严格意义上讲这可能算不上是语言,个人感觉更加像是正则表达式一般。

VFL写约束

我们在NSLayoutConstraint类里面还可以看到一个API:

/* Create an array of constraints using an ASCII art-like visual format string.*/
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary *)metrics views:(NSDictionary *)views;

这个API支持VFL,返回一组约束(注意是一组约束)。简单解释参数:

format:VFL字符串
opts:约束的一些格式 (目前我还没有具体用到实战中)
metrics:这个字典是自定义的,key可以写在format中,编译器解析时,自动替换为metrics字典中的value。
views:需要约束的所有view.

再次实战,我们将上面的需求用VFL实现:

//绑定需要约束的view 这里只有一个
NSDictionary *bindingDict = NSDictionaryOfVariableBindings(codeView);

//水平方向
NSString *HorizitalVFL = @"H:|-30-[codeView]-50-|";
NSArray *HorizitalArr = [NSLayoutConstraint constraintsWithVisualFormat:HorizitalVFL options:0 metrics:nil views:bindingDict];
[self.view addConstraints:HorizitalArr];

//垂直方向
NSString *VerticalVFL = @"V:|-30-[codeView(==40)]";
NSArray *VerticalArr = [NSLayoutConstraint constraintsWithVisualFormat:VerticalVFL options:0 metrics:nil views:bindingDict];
[self.view addConstraints:VerticalArr];

VFL的语法可以详细见官方文档

解释一下上面的代码:

1.NSDictionary *bindingDict = NSDictionaryOfVariableBindings(codeView)

绑定需要约束的view,可以是一个也可以是多个

2.@"H:|-30-[codeView]-50-|"

H:水平方向,|:superView,-:就是一种符号,可以想象成IB中的那些线条,-30-:30个像素,[]:中括号中的codeView是具体要进行约束的view对象。

所以上面的意思就是:水平方向上,codeView的左侧距离superView30像素,codeView的右侧距离superView50像素

3.@"V:|-30-[codeView(==40)]"

V:垂直方向,[codeView(==40)]中的(==40)说明codeView自身高度约束为40不变

所以上面的意思是:垂直方向上,codeView的顶部距离superView30像素,codeView本身的高度是40像素

4.[NSLayoutConstraint constraintsWithVisualFormat: options: metrics: views:]

用这个方法构造constraits数组。

可以看到用VFL写的约束要比constraintWithItem写的代码量要少很多,而且如果熟悉了VFL之后,理解起来会更加方便。
除此之外,github上还有Masonry这个库,是对Constraint的封装,有兴趣的同学可以去看看。个人认为还是用VFL比较好。

简单总结

通过上面的demo,我相信你可能初步进入了Constraint的世界。现在简单的总结一下:

1.忘记Frame, 是时候和Frame说拜拜了

2.每一个维度(垂直或者水平)上只少有两个约束

3.尽量使用IB而不是用代码来自动布局,除非一些特殊情况例如动画等。因为IB所见即所得,而且它会随时纠正你在布局上的错误

4.记住在手写约束一定要让需要约束的viewtranslatesAutoresizingMaskIntoConstraints == NO

5.property的customView,在用VFL时候要用[_customView]进行约束,而不能用[self.customView]

本文的demo工程已上传github。


本篇拙作较为基础,有不对的地方欢迎指正,接下来会写一篇在实际项目中的autoLayout遇到的一些坑和动画问题。

© 著作权归作者所有

共有 人打赏支持
hejunbinlan
粉丝 40
博文 532
码字总数 21018
作品 0
浦东
高级程序员
Autolayout优秀的第三方开源库

今天才发现CSDN支持markdown了…还是给出新博客地址:Autolayout优秀的第三方开源库 最近项目开始用纯代码布局整个UI框架, 对于前一段很长时间都是xib+storyboard狂拖控件约束的我来说,每天写...

humingtao2013 ⋅ 2015/06/25 ⋅ 0

Autolayout使用小结(一)

Autolayout使用小结(一) 最近做项目时,因为iPhone6和iPhone6Plus的兼容,我们启用了Autolayout. 以前是因为不用也能满足需求,也是因为懒,没有认真使用,只是了解过。经过一段时间的使用...

法斗斗 ⋅ 2015/10/14 ⋅ 0

从此爱上iOS Autolayout

这篇不是autolayout教程,只是autolayout动员文章和经验之谈,在本文第五节友情链接和推荐中,我将附上足够大家熟练使用autolayout的教程。这篇文章两个月前就想写下来,但因为一直工作较多,...

hejunbinlan ⋅ 2016/08/01 ⋅ 0

translatesAutoresizingMaskIntoConstraints 详解

translatesAutoresizingMaskIntoConstraints 把 autoresizingMask 转换为 Constraints 即:可以把 frame ,bouds,center 方式布局的视图自动转化为约束形式。(此时该视图上约束已经足够 不...

iOS_愛OS ⋅ 2017/12/28 ⋅ 0

Autolayout小结(二)

Autolayout小结(二) 在Autolayout小结(一)中介绍了在Autolayout学习中一些基本的注意点,本文会针对一些布局上常见的问题进行分析。 如何自动适应cell的高度 如何在ScrollView中使用Autol...

法斗斗 ⋅ 2015/10/14 ⋅ 0

哪里写Autolayout布局最合适?

用户需求一览 申请者fengmingxiao 项目大致代码行数500 项目 GitHub 地址 项目备注 探究UI布局方面的代码应该怎么写才是规范的,希望您能够 看看Readme,万分感谢,这不是什么大工程,但是我...

hejunbinlan ⋅ 2016/07/29 ⋅ 0

IOS 学习---storyboard 自动布局

autoResizing: 一共六条约束。 四周的四条约束:用于保证和其他视图的距离保持不变(如果勾选保持不变) 中间的两条约束:用于是否允许该视图可以缩放(如果勾选,则可以缩放) 缺陷:如果在...

fengyun321 ⋅ 2015/09/14 ⋅ 0

ios8 iOS Auto Layout

引言: Auto Layout是iOS6发布后引入的一个全新的布局特性,其目的是弥补以往autoresizing在布局方面的不足之处,以及未来面对更多尺寸适配时界面布局可以更好的适应. 要完全掌握Auto Layout是一...

AK_47 ⋅ 2014/10/31 ⋅ 0

AutoLayout中的Content Hugging 和 Content Compression

OS6中引入了AutoLayout,极大的方便了UI元素的布局,现在已经过去一年了,并且大部分设备的系统也已经升级到了iOS6,是时候要使用此项技术了。 在AutoLayout的学习中有两个概念官方文档讲述的...

hejunbinlan ⋅ 2016/07/15 ⋅ 0

AutoLayout框架Masonry使用心得

我们组分享会上分享了页面布局的一些写法,中途提到了AutoLayout,会后我决定将很久前挖的一个坑给填起来(还有好多坑就不说了,说了不填更毁形象了)。 可使用的框架首推Masonry,关于为啥选...

北方人在上海 ⋅ 2016/05/25 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

笔试题之Java基础部分【简】【一】

基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语法,集合的语法,io 的语法,虚拟机方面的语法,其他 1.length、length()和size() length针对...

anlve ⋅ 23分钟前 ⋅ 2

table eg

user_id user_name full_name 1 zhangsan 张三 2 lisi 李四 `` ™ [========] 2018-06-18 09:42:06 星期一½ gdsgagagagdsgasgagadsgdasgagsa...

qwfys ⋅ 48分钟前 ⋅ 0

一个有趣的Java问题

先来看看源码: public class TestDemo { public static void main(String[] args) { Integer a = 10; Integer b = 20; swap(a, b); System.out......

linxyz ⋅ 52分钟前 ⋅ 0

十五周二次课

十五周二次课 17.1mysql主从介绍 17.2准备工作 17.3配置主 17.4配置从 17.5测试主从同步 17.1mysql主从介绍 MySQL主从介绍 MySQL主从又叫做Replication、AB复制。简单讲就是A和B两台机器做主...

河图再现 ⋅ 今天 ⋅ 0

docker安装snmp rrdtool环境

以Ubuntu16:04作为基础版本 docker pull ubuntu:16.04 启动一个容器 docker run -d -i -t --name flow_mete ubuntu:16.04 bash 进入容器 docker exec -it flow_mete bash cd ~ 安装基本软件 ......

messud4312 ⋅ 今天 ⋅ 0

OSChina 周一乱弹 —— 快别开心了,你还没有女友呢。

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @莱布妮子 :分享吴彤的单曲《好春光》 《好春光》- 吴彤 手机党少年们想听歌,请使劲儿戳(这里) @clouddyy :小萝莉街上乱跑,误把我认错成...

小小编辑 ⋅ 今天 ⋅ 8

Java 开发者不容错过的 12 种高效工具

Java 开发者常常都会想办法如何更快地编写 Java 代码,让编程变得更加轻松。目前,市面上涌现出越来越多的高效编程工具。所以,以下总结了一系列工具列表,其中包含了大多数开发人员已经使用...

jason_kiss ⋅ 昨天 ⋅ 0

Linux下php访问远程ms sqlserver

1、安装freetds(略,安装在/opt/local/freetds 下) 2、cd /path/to/php-5.6.36/ 进入PHP源码目录 3、cd ext/mssql进入MSSQL模块源码目录 4、/opt/php/bin/phpize生成编译配置文件 5、 . ./...

wangxuwei ⋅ 昨天 ⋅ 0

如何成为技术专家

文章来源于 -- 时间的朋友 拥有良好的心态。首先要有空杯心态,用欣赏的眼光发现并学习别人的长处,包括但不限于工具的使用,工作方法,解决问题以及规划未来的能力等。向别人学习的同时要注...

长安一梦 ⋅ 昨天 ⋅ 0

Linux vmstat命令实战详解

vmstat命令是最常见的Linux/Unix监控工具,可以展现给定时间间隔的服务器的状态值,包括服务器的CPU使用率,内存使用,虚拟内存交换情况,IO读写情况。这个命令是我查看Linux/Unix最喜爱的命令...

刘祖鹏 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部