(转)AutoLayout深入浅出一[前传]

原文地址:http://grayluo.github.io/WeiFocusIo/autolayout/2015/01/24/autolayout1/

我想大部分的iOS开发者都面临着iOS设备分辨率碎片化而带来的一系列适配问题,以前的Rect布局方式显得越来越古板,越来越无法高效的完成我们布局的想法,不论是使用xib还是code进行布局都要无休止的使用分辨率进行调整,所有的布局完全相对于分辨率,新手一般会把布局写的比较死,而老手会把布局使用分辨率作为基点,但面对复杂的布局,总是感觉应该变革,为懒惰的程序员减少工作量,为其腾出更多时间来极客。这里我就和大家一起来把iOS的布局系统的研究下,一方面是为自己完善露点,另一方面也希望能够帮助到someone. 这里先来第一篇,引入AutoLayout。

我们一起来回顾一下iphone的分辨率:

  • 3GS : 480x320
  • 4(S) : 960x640
  • 5(S) : 1136x640
  • 6 : 1334x750
  • 6 Plus : 1920x1080

可以看到从2009年iphone3GS发布一直到2014年iphone plus发布,iphone的分辨率经历了各种变迁,在iphone6发布以前坚持了横向的宽度,这让我们这种开发者无需过多的 考虑适配问题,但android阵营的大屏手机来势汹汹,不断蚕食iphone的高端客户,为了迎合已经被”惯坏”的消费者的大屏需求,Apple也逐渐加快了大屏的投入,消费者满意了,但是开发者确会很苦逼,其实这些Apple看的很清楚,Apple是一家非常非常重视开发者的公司,iphone的成功归根结底不是长的好看,不是做工优良,而是有app store,而app store是由全球的iOS开发者贡献,Apple深知全球的iOS开发者是推动iphone保持热度的动力,为了使开发者能够更好的为iphone产业链做贡献,必然会为开发者提供自己能够提供的最好的工具,不得不说Apple的工程师们非常聪明,不断的创造新的理念,新的设计,在iphone设备间的适配方式也是Apple工程师们最急需为开发者解决的问题,现在我们就一起来看一下iOS布局方式的演变。

一、Rect

确定一个View的展现,需要一个原点和长宽,所以一开始的布局都是以此定律来写:

UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 300, 300)];
view.backgroundColor = [UIColor lightGrayColor];

UIView *subView1 = [[UIView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
[view addSubview:subView1];

[self.view addSubview:view];

但是我们希望view在高分辨率下能够变大,而低分辨率下变小,如何办?我们可能会这样:

CGFloat hMargin = 50;
CGFloat vMargin = 100;
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width-hMargin, self.view.bounds.size.height-vMargin)];
view.backgroundColor = [UIColor lightGrayColor];

CGFloat subMargin  = 10;
UIView *subView1 = [[UIView alloc]initWithFrame:CGRectMake(subMargin, subMargin, view.bounds.size.width-subMargin, view.bounds.size.height-subMargin)];
[view addSubview:subView1];

[self.view addSubview:view];

即将所有的view的frame都基于controller的root view的大小,而controller会根据分辨率自动确定其大小,所以我们添加在root view上的sub views都基于root view的大小进行相对布局,即一层一层的相对于父视图的大小,利用边界宽度,利用父视图的长宽来确定子视图的尺寸。 当多个并列的视图要同时添加到一个父视图时,我们可能会根据设计计算出各个视图的比例,确定边界,然后在添加view时利用计算出的比例将父视图的可见窗瓜分,这就像一块蛋糕,我们先要确定有多少人就可以确定有多少份,然后需要确定每一份的占比(蛋糕当然不需要算占比,just a simile)。 但是也有很多时候,很多的代码并不会按这样的思路一直写下去,这样一层一层的计算真的很累,而且需要设计师配合,这对懒惰的程序员不算是一个好的消息,于是我们可能会用autoresizingMask来解决:

二、autoresizingMask 我们看一下如下代码的效果:

UIView *view = [[UIView alloc]initWithFrame:CGRectMake(10, 10, self.view.bounds.size.width-10*2, 100)];
view.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:view];

 

运行,旋转,你会发现竖屏时,显示正常,但是横屏时你会发现宽度没变,距离右侧距离变大了, 此时有两种做法,要么在旋转事件中重新对视图进行布局调整,要么就使用autoresizingMask来解决,前者一想就知道工作量繁琐,容易出错,会花不小时间精力。我们肯定会选择后者。 我们添加一句:

view.autoresizingMask = UIViewAutoresizingFlexibleWidth;

 

你会发现神奇的事情发生了,它横屏、竖屏时左右间距都对了,那什么是autoresizingMask呢?
顾明思意,就是自动调整视图相对于父视图的位置大小,我们来看一下autoresizingMask的定义

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

其中:

UIViewAutoresizingNone 不会随父视图的改变而改变
UIViewAutoresizingFlexibleLeftMargin 自动调整view与父视图左边距,以保证右边距不变
UIViewAutoresizingFlexibleWidth 自动调整view的宽度,保证左边距和右边距不变
UIViewAutoresizingFlexibleRightMargin 自动调整view与父视图右边距,以保证左边距不变
UIViewAutoresizingFlexibleTopMargin 自动调整view与父视图上边距,以保证下边距不变
UIViewAutoresizingFlexibleHeight 自动调整view的高度,以保证上边距和下边距不变
UIViewAutoresizingFlexibleBottomMargin 自动调整view与父视图的下边距,以保证上边距不变

默认为UIViewAutoresizingNone,需要注意的是这个枚举定义的字面意思是调整的部分,比如UIViewAutoresizingFlexibleWidth调整的就是宽度,而不是说宽度不变化,这里描述的是变的部分,就跟汽车里面的ESP一样,有一个关的开关,按下去就表示关,所以需要注意。(提醒车友千万千万保持ESP处于开的状态,现代汽车就是这些电子器件保证了驾车的便捷与安全,它可以救你无数次)。
autoresizingMask在使用时,其值并不限于定义的值,可以组合,如下保证上边距和左边距不变,view将自动调整与下边距与右边距。 上面的示例中我们使用了UIViewAutoresizingFlexibleWidth,其实我们也可以组合使用如:

view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;

这两个值相或的结果就可以保证view的长宽随分辨率和旋转自动调整长宽,从而使view距离其父视图上下左右的边距保持不变。 在xib或者Storyboard(以后都叫SB,^_^)中布局时,切换至size insepector时可以看到Autoresizing,可以直接直接激活或者disable掉某一值。 看起来像不像给View加了一个弹簧(Springs),然后与其父视图之间以margins进行标记(struts),autoresizingMask其实就是我们后面讲autolayout时所要提到的Struts&Spring模式。

我们一起来用autoresizingMask完成我们一个布局设置:

  

保持红色view左上边距,保持橙色右上边距,保持蓝色view底部与左右边距。 我们得到的竖屏和横屏的效果如下:

 

我们看到红色和橙色还行,但是蓝色视图与上面的视图明显重叠了,这可不是我们想要的。 autoresizingMask非常聪明,已经非常尽力了,但是确实力不从心,因为super view只告诉它缩放子view以保持边距margin,但是并没有告诉它要缩放多少,view之间无法约束,就是说view之间没有padding,这就是autoresizingMask最大的缺点,它是无能为力了,只能通过屏幕改变时再重新计算视图,重新在viewWillLayoutSubviews/ viewDidLayoutSubviews这种布局事件中人为干预。

autoresizingMask的用法基本就是这样子,就是系统可以帮助我们确定某一视图相对于其父视图如何调整,但是同一父视图下的子视图之间的相对关系如何确定呢?比如我们建立两个两个子视图,难道只能像上面所讲的那样来计算视图模块,然后算出比例进行分配吗?在autolayout出来之前,可能很多人都会这样认为,但iOS6出来过后,Apple及时的添加了Autolayout用来满足开发者的布局要求。

三、AutoLayout

听这名字,高大山,自动布局,是不是真的如其名,我们逐步揭晓,我们接下来会先讨论在IB中使用autolayout,然后再讲如何在代码中使用autolayout,再然后我们会把autolayout使用过程中几种比较复杂的情况讨论下如何处理从而完成对autolayout的系统学习。

四、Size Class

尺寸分类?光看名字可能还不能理解它的意思,其实size class是iOS8引入的概念,在xcode6引入,设备太多,如果按固有的布局方式来进行调整,工作量会越来越大,工作越来越繁琐,从而Apple简化了分辩率的概念,将尺寸抽象为Compact、Regular、Any(紧凑、正常、任意)三种情况,在autolayout布局无法适应不同尺寸下的布局时,就需要单独为每种模式添加autolayout的约束。Autolayout讨论完过后,我们会一起来讨论Size Class。

时间: 2024-07-31 13:35:02

(转)AutoLayout深入浅出一[前传]的相关文章

Vue.js 入门指南之“前传”(含sublime text 3 配置)

题记:关注Vue.js 很久了,但就是没有动手写过一行代码,今天准备入手,却发现自己比菜鸟还菜,于是四方寻找大牛指点,才终于找到了入门的“入门”,就算是“入门指南”的“前传”吧.此文献给跟我一样“白痴级别”的前端开发人员,大牛请绕过. 1,下载安装Node.js 去 Node.js 官网下载一个Windows环节的安装包 node-v6.2.0-x64.rar 文件,一路安装下去即可.官网访问很慢,可以试试中文网 http://nodejs.cn/ 2,配置Vue环境 一开始看<基于Webpac

前传——一个文科生转行做编程的参考样本

动笔                                                                                                                                                                                      听闻前辈讲述写博客的诸多好处,早有开始写「技术博客」的想法,但囿于自身浅薄的「技术」,以及轻微的强迫症——总想准备充分后下笔——比如

大数据Spark蘑菇云前传第15课:Scala类型参数编程实战及Spark源码鉴赏(学习笔记)

前传第15课:Scala类型参数编程实战及Spark源码鉴赏 本課課程: Spark源码中的Scala类型系統的使用 Scala类型系統编程操作实战 Spark源码中的Scala类型系統的使用 classOf[RDD[_]] 這個也是类型系統 這里的意思是說 B 這種類型必需至少是 A 這樣類型 Ordering Scala类型系統编程操作实战 作為類型系統最大的就可以對類型進行限制,在Scala 中的類型系統,他本身也作為對象.e.g. 我們可以建立 Person 這個類,現在可以建立一個什麼

SpringMVC深度探险(一) —— SpringMVC前传

本文是专栏文章(SpringMVC深度探险)系列的文章之一,博客地址为:http://downpour.iteye.com/blog/1330537 在我们熟知的建立在三层结构(表示层.业务逻辑层.持久层)基础之上的J2EE应用程序开发之中,表示层的解决方案最多.因为在表示层自身的知识触角很多,需要解决的问题也不少,这也就难免造成与之对应的解决方案层出不穷. 笔者在很多讨论中经常可以看到类似“某某框架已死”,或者“某某框架已经足以打败所有其他的框架”的言论.事实上,每一种解决方案都有着自身独有的

uboot学习前传

uboot的学习前传 为什么要有uboot uboot主要作用是启动操作系统内核的. uboot还要负责部署整个计算机系统. uboot中还要有操作flash等板子上的硬件驱动 uboot还得提供一个命令行让我们可以在命令行下可以进行操作 uboot就是干以上或者更多事情的一个裸机程序而已. 计算机系统组成的三个重要部件: CPU + 外部存储器(flash/硬盘) + 内部存储器(内存, DDR/SDRAM/SRAM) PC机的启动过程: 在静态时,BIOS程序放在norflash中,在PC机

止增笑耳的星际迷航前传

假期闲来无事,看了几部影碟,其中尤以星际迷航11前传(2009年上映)不得不让人吐槽.这么一部漏洞百出的糟粕,在豆瓣评分居然还高达7.9分,实在是让人有些诧异豆瓣文青的水平. bug实在太多,现将之细数如下,欢迎补充: 1.企业号的形状根本就不是一个战舰,在这一集里居然还能攻击其他星舰,而其他剧集里都是比较强调它的速度,曲速前进什么的,这才是对的: 2.Kirk船长等人,哦,不,应该叫神啊,到任何星球执行任务居然都能像在地球一样随便呼吸,完全抛开头套: 3.这个系列的所有剧集都完全没有重力差异的

无应答前传消息流程

无应答前传 激活无应答呼叫前转业务后,当电话响铃无人接听,电话会自动转移到事前设定的号码上. INVITEsip:[email protected]:5060;transport=udp SIP/2.0 Via: SIP/2.0/UDP192.168.11.137:5060;rport;branch=z9hG4bK1075141334 From: "1003"<sip:[email protected]:5060;user=phone>;tag=387369502 To:&

Vue.js 入门指南之“前传”

题记:关注Vue.js 很久了,但就是没有动手写过一行代码,今天准备入手,却发现自己比菜鸟还菜,于是四方寻找大牛指点,才终于找到了入门的“入门”,就算是“入门指南”的“前传”吧.此文献给跟我一样“白痴级别”的前端开发人员,大牛请绕过. 1,下载安装Node.js 去 Node.js 官网下载一个Windows环节的安装包 node-v6.2.0-x64.rar 文件,一路安装下去即可.官网访问很慢,可以试试中文网 http://nodejs.cn/ 2,配置Vue环境 一开始看<基于Webpac

grep前传之侠影之谜 (Grep begins)

一.自述 我叫grep,是同你们一样,生活在都市里.每天做着重复的事,忙碌着. 说起来这个好笑,我生活的国家叫Bourne-Again Shell,因为我国来往人多,大家读者不便,于是心口不一的叫它"bash".bash,呵呵,好像被狠狠的砸了一下. 眼前这座烂尾楼便是我藏身的地方,它叫/bin,这里的居民都是上班族,那天下班后我数了一下用户,发现114个住户!天哪,好一座危楼! 在这个国家每家每户都供奉着一尊雕像,它叫root,对了,我和我的家人(group)都属于它,无理由的信奉着