本篇博客是本人在学习自己主动布局过程中对自己主动布局的理解和整理,分三部分介绍,内容可能会有所反复。见谅。
一、autosizing与Auto Layout对照,及Auto Layout简单介绍
1、springs&struts简单介绍及问题
你肯定非常熟悉autosizing masks-也被觉得是springs&struts模式。autosizing mask决定了当一个视图的父视图大小改变时,其自身须要做出什么改变。它有一个灵活的或固定不变的margins(struts)吗?它的宽和高要做出什么改变(springs)?
可是毕竟不像自己主动布局一样对全部视图进行全面约束,在某些情况下的不能依照开发人员的意愿完美的布局视图,比如http://www.cocoachina.com/industry/20131203/7462.html中第一个样例就是autosizing布局失败的样例,此时须要在viewWillLayoutSubviews中依据设备方向对frame进行设定,可是适配是一个令人很烦躁的事情。稍不注意就会出错,所以产生了后来的自己主动布局。
2、自己主动布局简单介绍及优势
自己主动布局使用约束去说明视图间的布局情况,使用约束最大的优势就是你再也不须要把时间浪费在坐标上了。
相反,你能够向自己主动布局描写叙述视图怎样和其它视图相关联,自己主动布局将会为你完毕全部困难的工作。这叫做依据目的设计(designing by intent)。
当你依据目的设计时,你表达的是你想要实现什么,而不须要关心它怎样实现。在autosizing中我们描写叙述布局时说”button的左上角坐标为(20,230)",如今你能够这么说了:”button是垂直居中于它的父视图。而且相对于父视图的左边缘有一个固定的距离”,使用这个描写叙述。无论父视图多大或多小,自己主动布局都能够自己主动计算出你的button须要在哪儿出现。这使得你用户界面的设置更具描写叙述性.你仅仅需简单的定义约束。系统会为你自己主动计算frames。
使用自己主动布局还有一个重要的优点就是本地化。
比方德语中的文本。出了名的比老奶奶的裹脚布还要长。适配起来是一件非常麻烦的事。再次。自己主动布局解救了猿,由于它能依据label须要显示的内容自己主动改变label的大小。
自己主动布局不仅对旋转有作用。它还能轻易的缩放你UI的大小从而适应不同尺寸的屏幕。
比如iphone5比iphone4s的高度变高了,程序在iPhone5中显示时是否会出现故障呢。不用操心。自己主动布局能轻易的拉伸你程序的用户界面。从而充满iPhone5垂直方向上多出来的空间。
注意:标示自己主动布局是否有效的T-bars是橘黄色时,意味着你的布局没有完毕,即自己主动布局没有足够的约束条件计算出视图的位置和大小。解决的方法便是添加很多其它约束,直到他们变蓝。
3、拥抱约束
1)、自己主动约束
假设你根本不提供不论什么约束,Xcode自己主动分配一套默认的约束,正是我们所知的自己主动约束。它会在程序built的编译时间中去完毕这些事。而不是设计时间。
当你设计你的用户界面时,Xcode5中的自己主动布局为了不參与你的设计方法而努力工作,这这是我们喜欢它的原因。自己主动约束为你的视图提供一个固定尺寸和位置。换句话说,视图总是拥有跟你在storyboard中看到的一样的坐标。这是很方便的,由于这就意味着你能够大量的忽视自己主动布局。你能够为那些拥有充分约束的控件不添加约束,仅仅为那些须要特殊规则的视图创建约束。
2)、Xcode创建自己主动约束的规则
Xcode仅仅为那些你没有设置不论什么约束的对象创建自己主动约束。
一旦你添加一个约束。你便是告诉Xcode你接管了这个视图。
Xcode将不再添加不论什么自己主动约束,并希望你为这个视图添加须要的约束。
3)、不完整约束的影响(约束过多或不足)
尽管Xcode5以后再也不强制你总是有一个有效的布局,可是执行一个无效布局的程序是不明智的,由于自己主动布局可能不能正确的计算须要将视图放在哪儿,要么视图的位置是不可预知的(约束不够)。要么程序将会崩溃(约束过多)。
4)、错位的视图
错位的视图,即依据自己主动布局显示视图的frame和你在屏幕上放置的视图的frame不在同一位置。此时依据自己主动布局显示视图的frame是橙色的虚线边框,你在屏幕上放置的视图的frame是橙色的实线边框(当谈到自己主动布局。橙色代表坏的)。
====假设你在屏幕上放置的视图的frame是你想要的位置,此时点击Editor菜单->Resolve Auto Layout Issues菜单->Update Constraints就能够更新约束。
二、自己主动布局介绍,及为何引入masonry
说明:在xib和storyboard中因为苹果将约束可视化呈现给开发人员使得自己主动布局非常easy使用;通过代码构建界面时假设想使用自己主动布局就必须通过苹果提供的接口创建约束。因为自己主动布局的接口不易使用。所以就产生了masonry,masonry将苹果提供的自己主动布局接口封装的易于使用。
1、Auto Layout是什么
Auto Layout是一个基于constraint(约束)的布局系统。它依据UI元素之间约束关系来调整UI元素的位置和大小。
2、Auto Layout解决什么问题
- 更easy适配不同分辨率设备的屏幕(iPhone 6 Plus, iPhone 6, iPhone 5s/5, iPhone 4s/4)
- 当设备旋转时不须要做额外处理
- 使用constraint来描写叙述布局逻辑,更利于理解和清晰
3、代码中怎样使用Auto Layout
Auto Layout中约束的类相应是NSLayoutConstraint, 而创建NSLayoutConstraint对象主要有两种方式,第一种是
+ (id)constraintWithItem:(id)view1
attribute:(NSLayoutAttribute)attribute1
relatedBy:(NSLayoutRelation)relation
toItem:(id)view2
attribute:(NSLayoutAttribute)attribute2
multiplier:(CGFloat)multiplier
constant:(CGFloat)constant;
上面方法主要意思是,某个view1的attribute1等于(小于或等于/大于或等于)某个view2的attribute2的multiplier倍加上constant。
而attribute主要由表示位置(上/下/左/右)和大小(宽/高)的下面几个值:
typedef enum: NSInteger {
NSLayoutAttributeLeft = 1,
NSLayoutAttributeRight,
NSLayoutAttributeTop,
NSLayoutAttributeBottom,
NSLayoutAttributeLeading,
NSLayoutAttributeTrailing,
NSLayoutAttributeWidth,
NSLayoutAttributeHeight,
NSLayoutAttributeCenterX,
NSLayoutAttributeCenterY,
NSLayoutAttributeBaseline,
NSLayoutAttributeNotAnAttribute = 0
} NSLayoutAttribute;
简化一下。使用公式能够表达为:
view1.attribute1 = view2.attribute2 * multiplier + constant
另外一种方式是:
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format
options:(NSLayoutFormatOptions)opts
metrics:(NSDictionary *)metrics
views:(NSDictionary *)views;
这样的方式主要是採用Visual Format Language(可视化格式语言)来描写叙述约束布局。尽管语法比較简洁。可是可读性比較差和easy出错。
4、Auto Layout存在问题
尽管Auto Layout在布局view方面是很强大和灵活,可是创建constraint的语法过于繁杂,引用Masonry一个样例:
UIView *superview = self;
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:@[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],
]];
如此简单的一个样例都要编写这么多行代码。想象一下假设创建多个view的constraint时会多么痛苦啊。
还有一个方式是採用Visual Format Language (VFL),尽管语法比較简洁,可是可读性比較差和easy出错。
5、为什么使用Masonry
Masonry是採用链式DSL(Domain-specific language)来封装NSLayoutConstraint,通过这样的方式编写Auto Layout布局代码更加易读和简洁。
6、Masonry怎样使用
使用Masonry创建constraint来定义布局的方式有三种:mas_makeConstraints。mas_updateConstraints,mas_remakeConstraints。
1). mas_makeConstraints
使用mas_makeConstraints创建constraint后,你能够使用局部变量或属性来保存以便下次引用它;假设创建多个constraints,你能够採用数组来保存它们。
// in public/private interface
@property (nonatomic, strong) MASConstraint *topConstraint;
...
// when making constraints
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);
make.left.equalTo(superview.mas_left).with.offset(padding.left);
}];
...
// then later you can call
[self.topConstraint uninstall];
2). mas_updateConstraints
有时你须要更新constraint(比如,动画和调试)而不是创建固定constraint,能够使用mas_updateConstraints方法
// this is Apple's recommended place for adding/updating constraints
// this method can get called multiple times in response to setNeedsUpdateConstraints
// which can be called by UIKit internally or in your code if you need to trigger an update to your constraints
- (void)updateConstraints {
[self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self);
make.width.equalTo(@(self.buttonSize.width)).priorityLow();
make.height.equalTo(@(self.buttonSize.height)).priorityLow();
make.width.lessThanOrEqualTo(self);
make.height.lessThanOrEqualTo(self);
}];
//according to apple super should be called at end of method
[super updateConstraints];
}
3). mas_remakeConstraints
mas_remakeConstraints与mas_updateConstraints比較相似,都是更新constraint。只是。mas_remakeConstraints是删除之前constraint。然后再加入新的constraint(适用于移动动画);而mas_updateConstraints仅仅是更新constraint的值。
- (void)changeButtonPosition {
[self.button mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.equalTo(self.buttonSize);
if (topLeft) {
make.top.and.left.offset(10);
} else {
make.bottom.and.right.offset(-10);
}
}];
}
想了解以上三个代码片段的很多其它细节,能够下载Masonry iOS Examplesproject查阅。
7、masonry使用举例
1)、三个控件等高
make.height.mas_equalTo(@[redView, blueView]);
2)、让控件始终居中显示
make.center.mas_equalTo(self.view);
3)、设置约束的优先级为最低
make.width.height.mas_equalTo(100 * self.scacle).priorityLow();
4)、让控件的宽和高小于或者等于self.view的宽和高
make.width.height.lessThanOrEqualTo(self.view);
8、此外自己主动布局的第三方库还有Classy。因为没实用过,就不再叙述了。有兴趣的话能够參考http://blog.csdn.net/zhang_red/article/details/45503683,或者谷歌一下。
三、Auto Layout使用总结
1、自己主动布局经常使用函数
1)、setNeedsUpdateConstraints
当一个自己定义view的某个属性发生改变。而且可能影响到constraint时,须要调用此方法去标记constraints须要在未来的某个点更新。系统然后调用updateConstraints.
2)、needsUpdateConstraints
constraint-based layout system使用此返回值去决定是否须要调用updateConstraints作为正常布局过程的一部分。
3)、updateConstraintsIfNeeded
马上触发约束更新。自己主动更新布局。
4)、updateConstraints
自己定义view应该重写此方法在当中建立constraints. 注意:要在实如今最后调用[super updateConstraints]
5)、updateViewConstraints
自己定义UIViewcontroller应该重写此方法在当中建立constraints. 注意:要在实如今最后调用[super updateConstraints]
2、要使用AutoLayout,请先设置要约束的view的translatesAutoresizingMaskIntoConstraints 属性为 NO 。
在xib或者sb中勾选Use Auto Layout。全部在xib或者sb中出现的view都已经默认将translatesAutoresizingMaskIntoConstraints设置为NO。
3、在使用AutoLayout布局的view中,代码中避免出现设置其frame相关属性(如center)的代码 ,可是能够获取其frame;
4、通过代码为xib或sb中view添加约束时。尽量避免在 viewDidLoad中运行,最好放在updateViewConstraints[UIViewcontroller]或者updateConstraints[UIView]中 ,记得调用[super updateViewConstraints]或者[super updateConstraints];
注意:在updateViewConstraints为view加入约束,请确保该view的translatesAutoresizingMaskIntoConstraints属性已设置为NO。假设你真的写在viewDidLoad里了。那么可能会遇到这样的崩溃错误Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Impossible to set up layout with view hierarchy unprepared for constraint.'
5、 假设须要在控制器中动态加入或者移除视图,在控制器中为新加入的视图添加约束,在updateViewConstrains中实现,然后调用[super updateViewConstrains];同理,在view中动态加入或者移除视图。在updateConstrains中实现,然后调用[super updateConstrains]
6、 控制器在其view须要又一次布局时会运行下面过程:
① 控制器的视图调整到新的尺寸 - 控制器会依据当前状态栏、导航条等其他因素的状态来调整其view的位置尺寸
② 假设没有使用autolayout,全部子视图会依据autoresizeing mask调整
③ 调用viewWillLayoutSubviews
④ 调用控制器视图的layoutSubviews,假设是使用autolayout,则会调用updateViewConstrains -> 该方法的实现会调用全部子视图的updateConstraints -> 更新完约束之后,全部视图会依据计算出来的新的布局更新位置
⑤ 调用控制器的viewDidLayoutSubviews
7、自己定义view须要又一次布局时会运行下面过程:
与使用springs and struts(autoresizingMask)比較。Auto layout在view显示之前。多引入了两个步骤:updating constraints 和laying out views。
每个步骤都依赖于上一个。display依赖layout,而layout依赖updating constraints。
updating constraints->layout->display
第一步:updating constraints。被称为測量阶段。其从下向上(from subview to super view),为下一步layout准备信息。
能够通过调用方法setNeedUpdateConstraints去触发此步。
constraints的改变也会自己主动的触发此步。可是,当你自己定义view的时候。假设一些改变可能会影响到布局的时候。通常须要自己去通知Auto layout,updateConstraintsIfNeeded。
自己定义view的话,通常能够重写updateConstraints方法。在当中能够加入view须要的局部的contraints。
第二步:layout,其从上向下(from super view to subview),此步主要应用上一步的信息去设置view的center和bounds。能够通过调用setNeedsLayout去触发此步骤。此方法不会马上应用layout。假设想要系统马上的更新layout,能够调用layoutIfNeeded。另外。自己定义view能够重写方法layoutSubViews来在layout的project中得到很多其它的定制化效果。
第三步:display,此步时把view渲染到屏幕上,它与你是否使用Auto layout无关。其操作是从上向下(from super view to subview),通过调用setNeedsDisplay触发。
由于每一步都依赖前一步,因此一个display可能会触发layout。当有不论什么layout没有被处理的时候,同理,layout可能会触发updating constraints,当constraint system更新改变的时候。
须要注意的是。这三步不是单向的。constraint-based layout是一个迭代的过程。layout过程中,可能去改变constraints,有一次触发updating constraints。进行一轮layout过程。
注意:假设你每一次调用自己定义layoutSubviews都会导致还有一个布局传递,那么你将会陷入一个无限循环中。