前言:这篇文章是笔者在项目中对布局技术进行技术选型和应用的相关介绍,供你们参考。
html
Question1:什么是autoLayout?android
Answer1: autolayout是苹果从iOS6开始推出的旨在优化、简化UI布局相关工做的新框架,其理念是抽象出约束的概念,将其做用于view,而再也不须要手动设置其frame。ios
我的理解其中的分别就好像面向对象编程和面向过程编程之间的分别;可以体会面向对象编程的好处,咱们也不难领会autolayout带来的变化:咱们惟一须要处理的,就是约束。其后的一切工做,苹果帮你搞定。git
Question2:为什么要引入自动布局?github
Answer2: 传统的frame设置方式在面对碎片化的屏幕分辨率显得力不从心;也难以应付旋转屏等高阶场景。约束的抽象让你从分辨率的适配工做中解脱出来,只须要了解(或者说抽象出)view所须要的约束,就能够得到一个理想的布局。不然,你须要计算不少难以理解和维护的硬编码,这不只不优雅,并且很容易出错,可读性和可维护性都相对较差。编程
当前iphone开发分辨率:api
iphone4/4s :320*480安全
iphone5/5c/5s: 320*568微信
iphone6: 375*667app
iphone 6 plus: 414*736
Autolayout技术:
从iOS6问世起(2012年下半年),苹果推出了autolayout技术,并开始大力推广其应用。Autolayout的api随着版本更迭不断的完善,业界也出现了不少第三方封装的自动布局api框架。咱们产品支持iOS7+,在系统支持方面是没有任何障碍的(注意不要使用iOS8+的api便可,后面会进一步说明)。
1. autoLayout相关技术概览:
4. Apple Interface Builder;
5. 第三方框架:masonry, purelayout, sdautolayout 等。
苹果推出的基础api,只要理解了autolayout的概念,也很容易理解其使用,但其缺点是接口很是冗长,使用麻烦,也缺乏组合的快捷接口,对于开发者来讲不够友好。
例(设置一个页面水平居中的约束):
[self.view addConstraint:[NSLayoutConstraint
constraintWithItem:btn
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
苹果推出的一种格式标记语言,专门用于描述布局关系。
例:
//水平方向
NSString *hVFL=@"H:|-20-[redView]-30-[blueView(==redView)]-20-|";
NSArray *hCons =[NSLayoutConstraintconstraintsWithVisualFormat:hVFL options:NSLayoutFormatAlignAllTop |NSLayoutFormatAlignAllBottom metrics:nilviews:@{@"redView":redView,@"blueView":blueView}]; [self.view addConstraints:hCons];
//垂直方向
NSString *vVFL=@"V:|-20-[redView(50)]";
NSArray *vCons =[NSLayoutConstraintconstraintsWithVisualFormat:vVFL options:0 metrics:nilviews:@{@"redView":redView}];
[self.view addConstraints:vCons];
该方式看上去比较科幻,须要对其格式标记语言有深刻了解以后才能够熟练使用;这种代码阅读起来主要靠脑补,画面太美。
根据资料显示,VFL语言对于某一些场景的自动布局不支持,且调试比较困难,可读性不好,学习成本高。
不推荐在项目中使用该技术(有兴趣的话能够用来学习和研究)。
AppleInterface Builder:
苹果的图形化编辑器。在其中能够方便、快速的设定约束(多用ctrl键)。
对于一次成型的view来讲,此方式的优点仍是显而易见的;推荐和xib一块儿使用。
Tips:
1. 建议对view进行命名,不然界面稍微复杂起来很容易将约束搞混。
2. 约束也能够做为outlet,用于后续变动,也能够用来作动画,很方便。
第三方框架:
第三方框架封装的出发点主要是但愿可以快速、可靠的使用代码来进行autolayout布局;在封装的过程当中尽量保持原生autolayout的所有能力。
最有名的框架是masonry,在github上1W+ stars.
https://github.com/SnapKit/Masonry
我在当前项目中引入的框架:pureLayout
https://github.com/PureLayout/PureLayout
masonry的特色是其简洁的链式语法;而pureLayout则更加保持了oc的语法习惯。这两个库都很是成熟。
之因此选择pureLayout,更多的是出于我的喜爱和习惯;在我一年多的使用过程和学习经验来看,该库的学习成本很是低,可以快速上手使用,api风格接近原生,也比较可靠稳定。
iOS 9.0+。
API对比示例:
// Creating constraints usingNSLayoutConstraint
[NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeadingMargin
multiplier:1.0
constant:0.0].active = YES;
[NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTrailingMargin
multiplier:1.0
constant:0.0].active = YES;
// Creating the same constraints usingLayout Anchors
UILayoutGuide *margin =self.view.layoutMarginsGuide;
[subview.leadingAnchorconstraintEqualToAnchor:margin.leadingAnchor].active = YES;
[subview.trailingAnchorconstraintEqualToAnchor:margin.trailingAnchor].active = YES;
能够看到,这里的api调用简单了不少。看来苹果也已经认识到了其api的冗长,对于开发者不够友好,所以对其作了二次封装。但此api 是iOS9+,所以咱们当前项目中没法使用。不过这不影响咱们自行去体验和学习其api。
官方文档:
务实的选择
罗列了上面的这些技术,咱们就能够大体的选择咱们的技术脉络了。NSLayoutAnchorSDK API由于系统限制,当前还不能使用;
NSLayoutConstraintSDK API虽然是原生,但开发成本太高,也不适合选择;
VFL语言有其固有缺陷(主要是可读性和维护性方面),也不建议在正式项目中使用;
咱们能够在项目中根据实际状况,结合使用第三方的框架(适用于代码编写的view)和Interface Builder(xib的view)这两种方式。而具体对于某一个view来讲,只使用一种方式(代码或者xib)来进行自动布局是相对安全且易于维护的方式。
约束类型介绍:
1. 长/宽,也就是其size
2. 设置距离:好比a view的最右边和b view的最左边之间的距离。
3. 设置居中(或居中的距离),好比 a view的x center和b view的 x center相等,或者二者之间的差值是多少。
4. iOS 8+苹果对view增长了margin(边缘)的概念,所以autolayout的2和3也相应的增长了边缘相关的api,即将以前的实际边界替换成边缘,而后再取理解就ok. 咱们的系统支持iOS7+,所以请务必不要使用此类api.
简单布局示例(使用purelayout):
- (void)setupButtonsConstraints
{
if ([self.buttonscount] < 2 && [self.buttonscount] > 0)
{
UIButton *button = self.buttons[0];
[button autoPinEdgesToSuperviewEdges];
}
else
{
[self.buttonsautoMatchViewsDimension:ALDimensionWidth];
[[self.buttonsfirstObject] autoPinEdgeToSuperviewEdge:ALEdgeLeft];
UIButton *previousView = nil;
for (UIButton * btn inself.buttons)
{
[btn autoAlignAxisToSuperviewAxis:ALAxisHorizontal];
[btn autoPinEdgeToSuperviewEdge:ALEdgeTop];
[btn autoPinEdgeToSuperviewEdge:ALEdgeBottom];
if (previousView)
{
[btn autoPinEdge:ALEdgeLefttoEdge:ALEdgeRightofView:previousView];
}
previousView = btn;
}
[[self.buttonslastObject] autoPinEdgeToSuperviewEdge:ALEdgeRight];
}
}
这里对一组button(其数量是变化的)设置了约束,它们宽度固定,水平排列,两两相连,左右都顶到了view的最边缘。
对于通常的view来讲,4个约束能够固定一个view。能够想象一下,你的view可否同时知足这几个条件,知足这几个条件之后它是否是就动不了了。
框架使用最佳实践:
学习和研究第三方库中的示例代码和api说明,可以帮助咱们快速的掌握其api使用;若是有必要能够深刻其源代码了解其中的细节。通常来讲,其示例代码中的api调用方式是相对标准和安全的,值得咱们参考和借鉴。
One more thing(关于autolayout的错误调试:)
autolayout错误会打印以下:
Unable to simultaneously satisfy constraints. Probably at least one of the constraintsin the following list is one you don't want. Try this: (1) look at eachconstraint and try to figure out which you don't expect; (2) find the code thatadded the unwanted constraint or constraints and fix it. (Note: If you'reseeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer tothe documentation for the UIView propertytranslatesAutoresizingMaskIntoConstraints) ( "<NSAutoresizingMaskLayoutConstraint:0x887d630 h=--&v=--& V:[UIButtonLabel:0x886ed80(19)]>", "<NSAutoresizingMaskLayoutConstraint:0x887d5f0 h=--&v=--& UIButtonLabel:0x886ed80.midY == + 37.5>", "<NSAutoresizingMaskLayoutConstraint:0x887b4b0h=--& v=--& V:[UIButtonLabel:0x72bb9b0(19)]>", "<NSAutoresizingMaskLayoutConstraint:0x887b470h=--& v=--& UIButtonLabel:0x72bb9b0.midY == - 0.5>", "<NSLayoutConstraint:0x72bf860V:[UILabel:0x72bf7c0(17)]>", "<NSLayoutConstraint:0x72c2430UILabel:0x72bfad0.top == UILabel:0x72bf7c0.top>", "<NSLayoutConstraint:0x72c2370UILabel:0x72c0270.top == UILabel:0x72bfad0.top>", "<NSLayoutConstraint:0x72c22b0V:[UILabel:0x72bf7c0]-(NSSpace(8))-[UIButton:0x886efe0]>", "<NSLayoutConstraint:0x72c15b0V:[UILabel:0x72c0270]-(NSSpace(8))-[UIRoundedRectButton:0x72bbc10]>", "<NSLayoutConstraint:0x72c1570UIRoundedRectButton:0x72bbc10.baseline ==UIRoundedRectButton:0x7571170.baseline>", "<NSLayoutConstraint:0x72c21f0 UIRoundedRectButton:0x7571170.top== UIButton:0x886efe0.top>" )
这里的约束看起来很难理解,应该是打印了相似VFL的约束表示。
我在项目中引入了NSLayoutConstraint+Description,重写了其description方法,这样在出错时能够容易的阅读当前出错的约束状况,加以鉴别。
附录:Autolayout出现前的iOS布局技术
关于旋转屏:
对于iphone应用开发者来讲,绝大部分应用都是不须要支持旋转屏幕的;苹果的系统应用却是有几个支持转屏(不过也主要是简单列表的布局,支持起来比较容易)。Iphone的长宽比例相差较大,所以若是要获得一个比较良好的体验,横屏和竖屏的布局通常都须要从新设计,由此带来了巨大的工做量;从用户体验来讲也看不到支持横屏的必要性。所以,对于iphone应用来讲,绝大部分应用都是不支持旋转屏幕的。你们能够看一下手机里的流行应用(微信、支付宝、网易新闻等),可能只有一些视频类应用的个别页面会支持转屏(播放视频横屏体验更佳)。
而对于iPad来讲,其长宽比例较为接近(4:3),且苹果一开始就建议iPad应用开发者适配横竖屏以提高用户体验(横屏使用iPad对用户来讲很日常,这点和iphone差异较大)。所以,对于iPad开发者来讲,由于转屏场景的存在,布局适配一直是开发中须要注意的一个重点问题。
固定frame写法:
写死frame来布局,在只有320*480一套分辨率的时候很是happy; frame无需计算; 在iphone5推出以前(2012年9月发布),写死frame来布局UI是很是方便快速的;而对于iphone开发者来讲,通常不须要考虑屏幕旋转的问题(上面咱们已经说了,大部分app是锁定屏幕方向的)。这个时代的iphone 开发者是很是幸福的(android开发者须要适配碎片化的分辨率)。但iphone五、iphone六、iphone6plus带来的分辨率碎片化让固定 frame写法增长了很大的工做量,且代码难以阅读和维护。
Autoresizing技术:
就是在建立视图的同时给出其相对于父视图的“对齐方式与缩放系数”,即autoresizingMask。当父视图发生变化时,经过每一个子视图的 autoresizingMask便可自动得出子视图的位置。
然而autoresizingMask的问题在于:
1. 其描述界面变化规则不够灵活,不少变化规则根本没法精确描述。autoresizingMask缩放比例是UIKit内部计算的,开发者没法指定缩放比例的精确值;且通常状况下,距离每每比比例更加精确(这点你们和设计同窗沟通的时候应该有体会)。
2. 变化规则只能基于父视图与子视图之间,没法创建同级视图或者跨级视图之间的关系。
总结:Autoresizing用来简单的指定和父view之间的关系仍是比较方便的。其在咱们当前的项目中使用的也比较多,但它的局限性决定了它只能做为frame布局的辅助,应用于一些简单场景,对于复杂多样化的布局力不从心。在autolayout问世之前,经过autoresizing也能够处理一些简单页面的旋转屏布局(在细节要求不高的状况下)。