在日常开发中我们经常会看到,当superview的bounds发生变化的时候,他的subview也会随之移动,但这是为什么呢?在superview的size发生变化的时候,subview内部发生了什么呢?看似简单的现象中其实包含了很多知识,估计再接下来很长的时间里我将仔细梳理关于Layout方面的知识,若大家在阅读过程中发现什么问题请及时纠正,共同进步😄
说起 layout 其实我们在开发过程中无时不刻不在用,特别是在写界面的时候,比如适配不同的设备大小,横屏竖屏转换的时候界面不能乱,有些 app 需要适配 iphone 和 ipad 两种设配的不同尺寸,还有tableview cell的内容自适应,有些时候当隐藏导航栏和现实导航栏的时候界面可能会伸缩或拉长等等,所以说 layout 在我们写界面是很有用的,因此他真的很重要。
Layout 有三种主要的表现形式。分别是:
- Manual Layout(手动布局):当 superview 的大小发生变化的时候,会接受到一个
layoutSubviews
的信息, 之后你通过重写layoutSubviews
方法来手动的重新为 subviews 布局,很明显这将会导致很大的工作量- Autoresizing(自动调整尺寸):这种方式是 iOS6 之前的一种自动布局方式。当 superview 的大小改变时,subview 会通过
autoresizingMask
属性来调整值。这个会在后面讲到- Autolayout(自动布局):它是在 iOS6 引进的, 主要通过 view 的
constraint
来实现的。 一个 constraint 其实就是一个NSLayoutConstraint
实例对象,通过数字来描述一个 view 相对于其他view 的大小和位置。这比autoresizeingMask
更精密更容易理解,一个 view 可以有多个 constraint, 并且他们可以描述任意两个 view 之间的关系。其实 auotolayout 在背后帮你实现了layoutSubviews
方法, 你可以不用手写代码来实现很多复杂的约束。你的 lauout 可以使用以上三种的任何一个或多个的组合,但一般来说我们很少会用手动布局,因为工作量大且效率较低。AutoresizeSubviews 默认会自动开启,除非你设置 superview 的
autoresizesSubviews
属性为 false,或者你使用了autolayout 则AutoresizeSubviews 将会被禁用。Autolayout 可以用在任何你想用的地方,是不是很方便呀。需要注意的是,在 Xcode7 及 7以后的版本中,autolayout 会在xib
或者storyboard
中默认勾选, 如果没有勾选的话则会使用autoresizng
而不是autolayout
哟!
Autoresizing
Autoresizing 主要是通过设置 subview 的
spring
和struts
属性来实现的。Spring 可以被拉伸,但是 strut 不可以,Spring 和 struts 可以在外部或内部,水平或垂直方向指定,因此你可以通过这两个属性来指定一个view要如何调整大小护着调整位置。
试想一下,一个 view 在他的 super的中心位置,当父视图调整大小时,view也会跟着调整大小。view 将会在其外部是固定(struts)的在内部是弹性(spring)的
继续设想有一个view在他的 superview 的中心位置,但是 view 不随父视图大小的变化而变化,这时view将会在外部 spring 内部 structs
再设想有一个OK按钮在他父视图的右下角位置,则 view 在内部是 struts,在他右方和下方是外部 struts,在上方和左方则是外部 spring。
最后设想有一个 textfield 在其 superview 的顶部,并且与 superview 同宽,这时他的外部是 struts, 但是下边是 spring, 谁知方向时内部 struts 竖直方向内部 spring
对于上面所讲到的几种情况大家可以先画图理解一下。在代码中
spring
和struts
是通过的 view 的autoresizingMask
属性来设置的。autoresizingMask 中的 options 是位掩码, 这些位掩码是在定义在一个 struckt 里面,这些位掩码表明了哪些是需要 spring 的,也就是说,没有专门设置 struts 的选项,只要没有设置,就是 struts 的。默认值是.None,也就意味着,所有的方向都是 struts,但是这是不可能的,当 superview 改变的时候,subview 肯定会改变,所以,.None 并不是所有都是 struts ,而是 .FlexibleRightMargin 和 .FlexibleBottomMargin 。
下面我门来举个例子,理解一下Autoresizing
let v1 = UIView(frame: CGRectMake(100,111,132,194))
v1.backgroundColor = UIColor.magentaColor()
let v2 = UIView(frame: CGRectMake(0,0,132,10))
v2.backgroundColor = UIColor.greenColor()
let v3 = UIView(frame: CGRectMake(v1.bounds.width-20, v1.bounds.height-20,20,20))
v3.backgroundColor = UIColor.redColor()
self.view.addSubview(v1)
v1.addSubview(v2)
v1.addSubview(v3)
运行一下你将会看到下图界面:
接下来我们为 v2 和 v3 分别添加 spring 和 struct 让他们看起来好像一个 textfild 在界面顶部, 一个 ok 按钮在界面的右下角
v2.autoresizingMask = .FlexibleWidth
v3.autoresizingMask = [.FlexibleTopMargin, .FlexibleLeftMargin]
v1.bounds.size.width += 40
v1.bounds.size.height -= 50
运行结果如下图:
上面这个例子可以看得出来 autoresizing 都做了哪些事情, 不过还是有点人为的改变superview的大小,在日常开发过程中 superview的大小变化通常都是自动的,不受人为控制,比如旋转屏幕到横屏状态,接下来我们通过改变v1的frame来达到想要的效果
v1.frame = self.view.bounds
v1.autoresizingMask = [.FlexibleHeight, .FlexibleWidth];
运行一下可以看到,v1充满了整个屏幕
我们再旋转一下屏幕可以看到
现在是横屏状态但 view 的布局并没有错乱,还是在正确的位置上
现在我们可以看出采用 autoresizing 布局还是蛮简单的,也正是因为他太过简单只能用来描述 superview 和 subview 之间的位置关系,无法描述view 与 view 之间的关系。在有 autolayout 之前,如果要使用 auto resizing 来实现比较复杂的布局还需要介入手动布局,调用
layoutsubviews
方法,这种方式一方面比较麻烦,另一方面还容易出错。所以还是让我们好啊后来研究一下 Autolayout 吧!看看它带给我们哪些便利!