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

关于AutoLayout,最早从iOS6开始引入使用。

主要功能是使用约束,对视图进行相对布局,以适应不同屏尺的变换。

网上大量的资料都在介绍xib和storyboard,如何使用AutoLayout,说纯代码使用AutoLayout进行UI布局的越来越少。对于我这个习惯了代码UI布局的人,写个备忘:

AutoLayout是什么?

使用一句Apple的官方定义的话

AutoLayout是一种基于约束的,描述性的布局系统。 Auto Layout Is a Constraint-Based, Descriptive Layout System.

关键词:

  • 基于约束 - 和以往定义frame的位置和尺寸不同,AutoLayout的位置确定是以所谓相对位置的约束来定义的,比如x坐标为superView的中心,y坐标为屏幕底部上方10像素等
  • 描述性 - 约束的定义和各个view的关系使用接近自然语言或者可视化语言(稍后会提到)的方法来进行描述
  • 布局系统 - 即字面意思,用来负责界面的各个元素的位置。

总而言之,AutoLayout为开发者提供了一种不同于传统对于UI元素位置指定的布局方法。以前,不论是在IB里拖放,还是在代码中写,每个UIView都会有自己的frame属性,来定义其在当前视图中的位置和尺寸。使用AutoLayout的话,就变为了使用约束条件来定义view的位置和尺寸。这样的最大好处是一举解决了不同分辨率和屏幕尺寸下view的适配问题,另外也简化了旋转时view的位置的定义,原来在底部之上10像素居中的view,不论在旋转屏幕或是更换设备(iPad或者iPhone5或者以后可能出现的mini iPad)的时候,始终还在底部之上10像素居中的位置,不会发生变化。 总结

使用约束条件来描述布局,view的frame会依据这些约束来进行计算 Describe the layout with constraints, and frames are calculated automatically.


AutoLayout和Autoresizing Mask的区别

Autoresizing Mask是我们的老朋友了…如果你以前一直是代码写UI的话,你肯定写过UIViewAutoresizingFlexibleWidth之类的枚举;如果你以前用IB比较多的话,一定注意到过每个view的size inspector中都有一个红色线条的Autoresizing的指示器和相应的动画缩放的示意图,这就是Autoresizing Mask。在iOS6之前,关于屏幕旋转的适配和iPhone,iPad屏幕的自动适配,基本都是由Autoresizing Mask来完成的。但是随着大家对iOS app的要求越来越高,以及已经以及今后可能出现的多种屏幕和分辨率的设备来说,Autoresizing Mask显得有些落伍和迟钝了。AutoLayout可以完成所有原来Autoresizing Mask能完成的工作,同时还能够胜任一些原来无法完成的任务,其中包括:

  • AutoLayout可以指定任意两个view的相对位置,而不需要像Autoresizing Mask那样需要两个view在直系的view hierarchy中。
  • AutoLayout不必须指定相等关系的约束,它可以指定非相等约束(大于或者小于等);而Autoresizing Mask所能做的布局只能是相等条件的。
  • AutoLayout可以指定约束的优先级,计算frame时将优先按照满足优先级高的条件进行计算。

总结

Autoresizing Mask是AutoLayout的子集,任何可以用Autoresizing Mask完成的工作都可以用AutoLayout完成。AutoLayout还具备一些Autoresizing Mask不具备的优良特性,以帮助我们更方便地构建界面。


AutoLayout基本使用方法

Interface Builder

这部分网上大量的教程,都是说的这个

手动使用API添加约束

创建

iOS6中新加入了一个类:NSLayoutConstraint,一个形如这样的约束

  • item1.attribute = multiplier ? item2.attribute + constant

对应的代码为

1 [NSLayoutConstraint constraintWithItem:button
2                              attribute:NSLayoutAttributeBottom
3                              relatedBy:NSLayoutRelationEqua
4                                 toItem:superview
5                              attribute:NSLayoutAttributeBottom
6                             multiplier:1.0
7                               constant:-padding]

这对应的约束是“button的底部(y) = superview的底部 -10”。

添加

在创建约束之后,需要将其添加到作用的view上。UIView(当然NSView也一样)加入了一个新的实例方法:

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

用来将约束添加到view。在添加时唯一要注意的是添加的目标view要遵循以下规则:

  • 对于两个同层级view之间的约束关系,添加到他们的父view上
  • 对于两个不同层级view之间的约束关系,添加到他们最近的共同父view上
  • 对于有层次关系的两个view之间的约束关系,添加到层次较高的父view上

刷新

可以通过-setNeedsUpdateConstraints和-layoutIfNeeded两个方法来刷新约束的改变,使UIView重新布局。这和CoreGraphic的-setNeedsDisplay一套东西是一样的~

Visual Format Language 可视格式语言

UIKit团队这次相当有爱,估计他们自己也觉得新加约束的API名字太长了,因此他们发明了一种新的方式来描述约束条件,十分有趣。这种语言是对视觉描述的一种抽象,大概过程看起来是这样的: accept按钮在cancel按钮右侧默认间距处

最后使用VFL(Visual Format Language)描述变成这样:

1 [NSLayoutConstraint constraintsWithVisualFormat:@\\"[cancelButton]-[acceptButton]\"
2                                         options:0
3                                         metrics:nil
4                                           views:viewsDictionary];

其中viewsDictionary是绑定了view的名字和对象的字典,对于这个例子可以用以下方法得到对应的字典:

1 UIButton *cancelButton = ...
2 UIButton *acceptButton = ...
3 viewsDictionary = NSDictionaryOfVariableBindings(cancelButton,acceptButton);

生成的字典为

{ acceptButton = ""; cancelButton = ""; } 

当然,不嫌累的话自己手写也未尝不可。现在字典啊数组啊写法相对简化了很多了,因此也不复杂。关于Objective-C的新语法,可以参考我之前的一篇WWDC 2012笔记:WWDC 2012 Session笔记——405 Modern Objective-C。 在view名字后面添加括号以及连接处的数字可以赋予表达式更多意义,以下进行一些举例:

  • [cancelButton(72)]-12-[acceptButton(50)]

    • 取消按钮宽72point,accept按钮宽50point,它们之间间距12point
  • [wideView(>[email protected])]
    • wideView宽度大于等于60point,该约束条件优先级为700(优先级最大值为1000,优先级越高的约束越先被满足)
  • V:[redBox][yellowBox(==redBox)]
    • 竖直布局,先是一个redBox,其下方紧接一个宽度等于redBox宽度的yellowBox
  • H:|-[Find]-[FindNext]-[FindField(>=20)]-|
    • 水平布局,Find距离父view左边缘默认间隔宽度,之后是FindNext距离Find间隔默认宽度;再之后是宽度不小于20的FindField,它和FindNext以及父view右边缘的间距都是默认宽度。(竖线‘|‘ 表示superview的边缘)

容易出现的错误

因为涉及约束问题,因此约束模型下的所有可能出现的问题这里都会出现,具体来说包括两种:

  • Ambiguous Layout 布局不能确定
  • Unsatisfiable Constraints 无法满足约束

布局不能确定指的是给出的约束条件无法唯一确定一种布局,也即约束条件不足,无法得到唯一的布局结果。这种情况一般添加一些必要的约束或者调整优先级可以解决。无法满足约束的问题来源是有约束条件互相冲突,因此无法同时满足,需要删掉一些约束。两种错误在出现时均会导致布局的不稳定和错误,Ambiguous可以被容忍并且选择一种可行布局呈现在UI上,Unsatisfiable的话会无法得到UI布局并报错。 对于不能确定的布局,可以通过调试时暂停程序,在debugger中输入

  • po [[UIWindow keyWindow] _autolayoutTrace]

来检查是否存在Ambiguous Layout以及存在的位置,来帮助添加条件。另外还有一些检查方法,来查看view的约束和约束状态:

  • [view constraintsAffectingLayoutForOrientation/Axis: NSLayoutConstraintOrientationHorizontal/Vertical]
  • [view hasAmbiguousLayout]
    • [view exerciseAmbiguityInLayout]

布局动画

动画是UI体验的重要部分,更改布局以后的动画也非常关键。说到动画,Core Animation又立功了..自从CA出现以后,所有的动画效果都非常cheap,在auto layout中情况也和collection view里一样,很简单(可以参考WWDC 2012 Session笔记——219 Advanced Collection Views and Building Custom Layouts),只需要把layoutIfNeeded放到animation block中即可~

1 [UIView animateWithDuration:0.5 animations:^{
2     [view layoutIfNeeded];
3 }];

部分代码

纯净代码UI正常布局后,添加autolayout就可以了,调整相当方便

这是一段水平居中,垂直并列的4个按钮 布局代码

setTranslatesAutoresizingMaskIntoConstraints  是为no,开启AutoLayou.

//-----autoLayout

[_btn_1 setTranslatesAutoresizingMaskIntoConstraints:NO];

[_btn_2 setTranslatesAutoresizingMaskIntoConstraints:NO];

[_btn_3 setTranslatesAutoresizingMaskIntoConstraints:NO];

[_btn_4 setTranslatesAutoresizingMaskIntoConstraints:NO];

CGSize winSize = [[iHappySDKSingle shareSingle] getScreenSize];

CGFloat tpo = _btn_1.frame.origin.y;

CGFloat hpod = _btn_1.frame.origin.x;

CGFloat btnH = _btn_1.frame.size.height;

CGFloat vpod = winSize.width*0.15-btnH;

NSNumber* tp = [NSNumber numberWithFloat:tpo];

NSNumber* hd = [NSNumber numberWithFloat:hpod];

NSNumber* vd = [NSNumber numberWithFloat:vpod];

NSNumber* bh = [NSNumber numberWithFloat:btnH];

NSNumber* btm = [NSNumber numberWithFloat:vpod*2];

NSDictionary *dict1 = NSDictionaryOfVariableBindings(_btn_1,_btn_2,_btn_3,_btn_4);

NSDictionary *metrics [email protected]{@"hPadding":hd,@"vPadding":vd,@"top":tp,@"btm":btm,@"btnHeight":bh};

NSString *vfl1 = @"|-hPadding-[_btn_1]-hPadding-|";

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl1

options:0

metrics:metrics

views:dict1]];

NSString *vfl2 = @"|-hPadding-[_btn_2]-hPadding-|";

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl2

options:0

metrics:metrics

views:dict1]];

NSString *vfl3 = @"|-hPadding-[_btn_3]-hPadding-|";

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl3

options:0

metrics:metrics

views:dict1]];

NSString *vfl4 = @"|-hPadding-[_btn_4]-hPadding-|";

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl4

options:0

metrics:metrics

views:dict1]];

NSString *vfl5 = @"V:|-(<=top)-[_btn_1(btnHeight)]-vPadding-[_btn_2(btnHeight)]-vPadding-[_btn_3(btnHeight)]-vPadding-[_btn_4(btnHeight)]-(>=btm)-|";

if (_btn_1.hidden) {

vfl5 = @"V:|-(<=top)-[_btn_2(btnHeight)]-vPadding-[_btn_3(btnHeight)]-vPadding-[_btn_4(btnHeight)]-(>=btm)-|";

}

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl5

options:0

metrics:metrics

views:dict1]];

纯净代码UI正常布局后,增加一个函数,进行自动布局

水平居中布局:NSLayoutAttributeCenterX

垂直居中布局:NSLayoutAttributeCenterY

以及后面的布局切换动画。

- (void)setAutoLayoutForKuang:(UIView*)imgv

{

UIView * view = self;

[imgv setTranslatesAutoresizingMaskIntoConstraints:NO];

NSDictionary *dict1 = NSDictionaryOfVariableBindings(imgv);

NSDictionary *metrics = @{@"width":[NSNumbernumberWithFloat:imgv.frame.size.width],

@"height":[NSNumber numberWithFloat:imgv.frame.size.height],

@"top":[NSNumber numberWithFloat:imgv.frame.origin.y]

};

NSString *vfl1 = @"[imgv(width)]";

[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl1

options:0

metrics:metrics

views:dict1]];

NSString *vfl2 = @"V:[imgv(height)]";

[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl2

options:0

metrics:metrics

views:dict1]];

[view addConstraint:[NSLayoutConstraint constraintWithItem:imgvattribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:viewattribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];

[view addConstraint:[NSLayoutConstraint constraintWithItem:imgvattribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:viewattribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];

//animation

[UIView animateWithDuration:0.25 animations:^{

[imgv layoutIfNeeded];

}];

}

-----------------------------------------

网上相关文章:

-----------------------------------------

1、AutoLayout(自动布局)入门    推荐

2、Autolayout及VFL经验分享      此文包含有一个demo(快速访问下载) 推荐

3、iOS 6 Auto Layout NSLayoutConstraint 界面布局

不是朋友在发愁手动些这些类似脚本的字符:

现在推荐一个开源库给大家

github:https://github.com/TelenLiu/Lyt

[email protected]: http://git.oschina.net/TelenLiu/Lyt

时间: 2024-08-02 22:59:57

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

IOS开发语言Swift入门连载---自动引用计数

IOS开发语言Swift入门连载-自动引用计数 Swift使用自动引用计数(ARC)这一机制来跟踪和管理你的应用程序的内存.通常情况下,Swift 的内存管理机制会一直起着作用,你无须自己来考虑内存的管理.ARC 会在类的实例不再被使用时,自动释放其占用的内存. 然而,在少数情况下,ARC 为了能帮助你管理内存,需要更多的关于你的代码之间关系的信息.本章描述了这些情况,并且为你示范怎样启用 ARC 来管理你的应用程序的内存. 注意 引用计数仅仅应用于类的实例.结构体和枚举类型是值类型,不是引用类

iOS开发——响应链(Responder Chain)的深入理解和代码示例

我在之前一篇博客<iOS响应者链Responder Chain浅析>中对iOS开发中遇到的响应者链概念有了基本的了解.但是仅仅停留在理解概念的基础上还是远远不够的.该博客我们会通过代码案例来深入理解响应链.该博客的示例上传至 https://github.com/chenyufeng1991/ResponderChain  . (1)首先来说说第一响应者(First Responder).响应事件的传递过程就是为了找到第一响应者.以下几个方法: isFirstResponder:判断该View

iOS开发,让数据更安全的几个加密方式

任何应用的开发中安全都是重中之重,在信息交互异常活跃的现在,信息加密技术显得尤为重要.在app应用开发中,我们需要对应用中的多项数据进行加密处理,从而来保证应用上线后的安全性,给用户一个安全保障.这篇文章就介绍在iOS开发中最常用的数据加密方式. 文中证书锁定内容部分参考了博客http://blog.csdn.net/dd864140130/article/details/52625666. iOS中数据加密的几方式 1.使用数字证书锁定来保证不被中间人拦截,将服务器返回的数据和我的当地证书进行

【iOS开发-50】利用创建新的类实现代码封装,从而不知不觉实践一个简单的MVC实验

接上次案例谈代码封装.上次案例见:[iOS开发-48]九宫格布局案例:自动布局.字典转模型运用.id和instancetype区别.xib重复视图运用及与nib关系 代码封装的原则是:要保证视图控制器尽量少的接触到其他对象的属性,也就是说,尽量把数据或者属性封装到一个类里面,然后利用类或者对象的方法来调用或者设置数据.而是赤裸裸地把属性都写在视图控制器中.核心作用在于:减少视图控制器的代码量,把数据和属性的处理封装起来,这样也便于其他视图控制器的使用. 要做到的结果就是如下(我们要根据数组里面的

iOS开发进阶 - 使用shell脚本自动打包上传到fir.im上-b

用fir.im测试已经好长时间了,感觉每次打包上传都很麻烦,想着是不是可以用脚本自动打包,在网上搜了一下确实有,下面总结一下如何使用脚本自动打包上传到fir.im,以及打包过程中遇到的问题和解决办法 相关资料和下载 首先是打包脚本的下载地址,这个是我找到的比较全的一个,里面有很多不同功能的shell脚本,亲测好用,传送门:https://github.com/heyuan110/BashShell?spm=5176.100239.blogcont5028.4.kFcLtR 还有关于fir指令的一

IOS开发—截屏

- (void)actionShot:(UIButton *)sender{ //可以隐藏按钮,渲染完后显示回来 self.buttonShot.hidden =YES; //创建图形上下文 UIGraphicsBeginImageContextWithOptions(CGSizeMake(self.view.frame.size.width,self.view.frame.size.height),NO, 0); //获取图形上下文并将当前屏幕渲染到图形上下文上 AppDelegate *de

iOS开发进阶 - 使用shell脚本自动打包上传到fir.im上

如果移动端访问不佳,可以访问我的个人博客 用fir.im测试已经好长时间了,感觉每次打包上传都很麻烦,想着是不是可以用脚本自动打包,在网上搜了一下确实有,下面总结一下如何使用脚本自动打包上传到fir.im,以及打包过程中遇到的问题和解决办法 相关资料和下载 首先是打包脚本的下载地址,这个是我找到的比较全的一个,里面有很多不同功能的shell脚本,亲测好用,传送门 还有关于fir指令的一些介绍可以去这里查看,传送门 第一步安装fir-cil fir-cli 使用 Ruby 构建, 无需编译, 只要

IOS开发学习笔记034-UIScrollView-循环自动滚动

实现scrollView的自动循环滚动,需要实现几个方法. 其中scrollView中始终保存三张图片,其他的图片在滚动到时再进行加载. 循环的实现主要是在setUpdate 中,如果索引为0是第一个,索引为2是最后一个,这是对索引值进行改变.第一个后接着显示最后一个,最后一个后接着显示第一个.依次循环. 分析过程为: 1.首先是set方法 setImageNames 1 #pragma mark - setter方法重写 2 - (void)setImageNames:(NSArray *)i

ios开发 Reflection(三) 利用反射自动绑值

反射的具体应用,自动绑值 获取属性列表 1 - (NSArray*)propertyKeys 2 { 3 unsigned int outCount, i; 4 objc_property_t *properties = class_copyPropertyList([self class], &outCount); 5 NSMutableArray *keys = [[NSMutableArray alloc] initWithCapacity:outCount]; 6 for (i = 0;