Autoresizing
Autoresizing是早期iOS设备机型很少、APP界面布局相对简单的背景下产生的一种屏幕适配技术。早期的iOS设备机型很少、屏幕尺寸单一、APP界面相对简单,屏幕适配并没有现在这么复杂,在当时这种背景下,产生了Autoresizing。当时这种情况下苹果推出Autoresizing也是可以理解的,但是如果放到现在这种大背景下,Autoresizing是不能够满足开发者的屏幕适配需求的,具体原因请见下文。
1.1.Autoresizing的启用
Xcode5之后,新建的项目默认使用AutoLayout。Autoresizing默认不启用,我们可以去掉use Auto Layout前面的对勾来启用Autoresizing,如下图。
image在Xcode8
中删除了size class
选项,取而代之的是Use Trait Variations
(特征变量)选项。
1.2.Autoresizing介绍
有一种说法:autoresizing是为了解决iPad开发中横竖屏适配问题应运而生的。代码中的autoresizingMask和storyBoard中尺寸检查器中的Autoresizing是一回事。iPhone5开始,Xcode添加了autolayout功能。storyBoard默认采用autolayout,取代了之前的autoresizing。如果使用autoresizing,需要在以下位置去掉“Use Auto Layout”。
1.2.1.storyboard中使用Autoresizing
image从上图看出,storyBoard中的的Autoresizing只能设置两个父子视图之间的相对位置关系,一共6条虚线,分别是周围的四条虚线和方块内部的两条线。周围的四条虚线分别代表子控件距离父控件上、下、左、右之间的距离关系/或者叫约束关系,周围的四条虚线所包围的小方块代表子视图,小方块内部的两条带双向箭头的线分别代表子控件的宽度和高度。
当我们点击周围四条虚线时,虚线会变成实线,代表子控件和父控件在这个方向上的间距被固定了。当我们点击子视图内部的虚线时,同样也变为实线,代表子视图的宽度或者高度被固定了。
举个例子:当我们点击最左边的虚线时候,代表子视图距离父视图左边的间距被固定了,而其他三个方向的距离和宽高会随父视图的缩放二缩放。
1.2.2.代码中使用Autoresizing
我们不仅可以在storyboard中使用Autoresizing来约束父子视图,也可以使用代码来设置父子视图之间的位置关系。UIView有一个autoresizingMask属性,可以通过该属性来约束父子视图之前的位置关系,并且UIView还有一个BOOL类型的autoresizesSubviews属性,默认为YES,代表父控件会跟随子控件尺寸的变化而变化。和frame、bounds、center、transform等属性一样,autoresizingMask和autoresizesSubviews也是属于UIView的几何分类-UIViewGeometry中的属性。
@property(nonatomic) BOOL autoresizesSubviews; // default is YES. if set, subviews are adjusted according to their autoresizingMask if self.bounds changes
@property(nonatomic) UIViewAutoresizing autoresizingMask; // simple resize. default is UIViewAutoresizingNone
查看注释,autoresizesSubviews属性的大意是:默认autoresizesSubviews = YES。如果UIView设置了autoresizesSubviews,那么他的子控件的bounds如果发生了变化,他的子控件将会根据子控件自己的autoresizingMask属性的值来进行调整。
那么autoresizingMask又是什么呢?
autoresizingMask是一个枚举值,作用是自动调整子控件与父控件中间的margin(间距)或者子控件的宽高。默认其枚举值是UIViewAutoresizingNone。如下是其全部的枚举值:
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
};
从上面给出的代码可以看出,autoresizingMask有7个枚举值,除了UIViewAutoresizingNone之外还有6个枚举值,分别对应storyboard中的那6条虚线。所以,storyboard和代码是相同的,无论什么视图,凡是可以通过storyboard进行设置的属性,都有与之对应的属性代码,我们也可以使用代码的方式实现。毕竟,storyboard中的属性最终还是会翻译成可执行的代码,只不过XCode利用可视化的storyboard工具帮助我们完成了代码完成的事情。
值得注意的是:autoresizingMask的枚举值是使用位移的形式给出的,这样设置的好处在于,当我们使用代码给某个视图设置autoresizingMask属性时,我们可以给autoresizingMask属性指定多个枚举值。指定方式如下:
[subView setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin |
![7.gif](https://img.haomeiwen.com/i1055199/b48a0ccf7076c294.gif?imageMogr2/auto-orient/strip)
FlexibleLeftMargin];
甚至我们可以使用这种方式:
[subView setAutoresizingMask:5];
系统自动把5分解成5 = 4 + 1。然后我们就可以知道:4 = 1 << 2 = UIViewAutoresizingFlexibleRightMargin;1 = 1 << 0 = UIViewAutoresizingFlexibleLeftMargin。所以上面的代码就被解释成:
[subView setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin]; // 子视图距离父视图左右间距不变,宽度随父视图宽度的缩放而缩放。
不难发现,这样以位移的方式指定autoresizingMask枚举值,也契合了storyboard中可以给子控件设置多个方向的约束的情景。如下图:
image注意:
Autoresizing只能设置父子视图之间的关系,也就是说,Autoresizing只能控制子视图和父视图之间的位置/大小关系。Autoresizing不能设置兄弟视图之间的关系,当然也不能设置完全不相关的两个视图之间的关系。正因为Autoresizing只能设置父子视图之间的关系,所以,Autoresizing只能应用于两个视图之间,不能应用于三个或者更多视图之间。毕竟,一个儿子不可能有两个亲爹。
注意:
UIView的autoresizesSubviews属性为YES时(默认为YES),autoresizingMask才会生效。也就是说,当我们想要利用autoresizingMask指定某个控件和其父控件的关系时候,必须autoresizesSubviews = YES。
注意:
值得注意的是,autoresizingMask的每个枚举值都带有Flexible
这个单词,其意思是:灵活的,弹性的。所以,我们在对其枚举值进行一一翻译。
UIViewAutoresizingNone // 就是不自动调整。
UIViewAutoresizingFlexibleLeftMargin // 自动弹性的调整与superView左边的距离,保证与superView右边的距离不变。
UIViewAutoresizingFlexibleRightMargin // 自动弹性的调整与superView的右边距离,保证与superView左边的距离不变。
UIViewAutoresizingFlexibleTopMargin // 自动弹性d调整与superView顶部的距离,保证与superView底部的距离不变。
UIViewAutoresizingFlexibleBottomMargin // 自动弹性的调整与superView底部的距离,也就是说,与superView顶部的距离不变。
UIViewAutoresizingFlexibleWidth // 自动弹性的调整自己的宽度,保证与superView左边和右边的距离不变。
UIViewAutoresizingFlexibleHeight // 自动弹性的调整自己的高度,保证与superView顶部和底部的距离不变。
看完翻译才恍然大悟,原来这些枚举值和storyboard中的虚线是相反的,当我们点击了storyboard中国的某个虚线后代表其间距被固定,而我们用代码设置则代表相反方向的间距被固定。
但是,例外的是,storyBoard中子控件内部的两个带箭头的虚线和枚举值UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
具有相同的意义。也就是说,当我们点击了storyBoard中子控件内带箭头的水平虚线使之变为实线时,就相当于[subView setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
当我们点击了storyBoard中子控件内带箭头的垂直虚线使之变为实线时,就相当于[subView setAutoresizingMask UIViewAutoresizingFlexibleHeight];
image注意:
简而言之,4条margin虚线代表设置autoresizingMask中的与之对应的4枚举值,而实线的width和height才代表设置autoreMask中与之对应的2个枚举值。
1.2.3.Autoresizing的组合场景
autoresizingMask枚举值及其对应的storyBoard预览效果说明
UIViewAutoresizingMaskNone
view的frame不会随superview的改变而改变,相当于frame(右图的xib中预览效果与实际效果有差,实际效果是view的上边距不变)
UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin
view和其superView左间距和上间距固定,宽高固定,右间距和底部间距随父控件的缩放而按比例缩放
UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin
view与其superView上间距固定,右间距固定,宽高固定,左间距、下间距锁父控件的缩放而缩放
UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin
view与其superView的右间距、底部间距固定,宽高固定,上间距、左间距随父控件的缩放而缩放
UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin
view与其superView的左间距、底部间距固定,宽高固定,右间距、上间距随父控件的缩放而缩放
UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleHeight
view与其superView的上间距、左间距、底部间距固定,宽度固定。高度、右边距随父控件缩放而缩放
imageUIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth
view与其superView的左间距、上间距、右间距固定,高度固定。宽度、底部间距随父控件的缩放而缩放
UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight
view与其superView的上间距、右间距、底部间距固定,宽度固定。高度、左间距随父控件的缩放而缩放
UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth
view与其superView的左间距、右间距、底部间距固定,高度固定。宽度、上间距随父控件的缩放而缩放
UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
view与其superView的左间距、上间距、底部间距固定。宽度、高度、右间距随父控件的缩放而缩放
UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
view与其superView的左间距、上间距、右间距固定。宽度、高度、底部间距随父控件的缩放而缩放
UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
view与其superView的上间距、右间距、左间距固定。宽度、高度、左间距随父控件的缩放而缩放
UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
view与其superView的左间距、底部间距、右间距固定。宽度、高度、上间距随父控件的缩放而缩放
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottom
view与其superView的宽高比例维持不变,上下左右间距也随其superView的缩放而缩放
UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin
view与其superView的左右间距固定,高度固定,宽度、上间距、底部间距随其父控件的缩放而缩放
UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin
view与其superView的上下左右边距的比例维持不变,宽高固定,反映在storyBoard中,就是什么都不设置
UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin
左边距、右边距、宽按比例调整,上边距固定,下边距固定,高度固定(右图的xib中预览效果与实际效果有差,实际效果是view的上边距不变)垂直方向是同样效果,故不列举
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
自动调整view的宽和高,保证上下左右边距不变。如把tableView设置为此属性,那么无论viewController的view是多大,都能自动铺满
不合理布局
UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin
view与其superView的左边距和右边距的比例维持不变,上下间距固定,宽高固定(下图的xib中预览效果与实际效果有差,实际效果是view的上边距不变)这种约束方式相当于上下间距固定,宽高固定,那么父控件高度缩放的时候就会产生冲突,所以这种布局方式是不合理的
UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin
view与其superView的上边距和下边距的比例维持不变,左右间距固定,宽高固定(这种约束方式相当于左右间距固定,宽高固定,那么父控件宽度缩放的时候就会产生冲突,所以这种布局方式也是不合理的)
UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth
view与其superView的左边距和width按比例调整,高度固定,右边距固定,上边距固定,下边距固定(下图的xib中预览效果与实际效果有差,实际效果是view的上边距不变)(这种约束方式相当于上下间距固定,高度固定,那么父控件高度缩放的时候就会产生冲突,所以这种布局方式也是不合理的)
综上发现,只要是我们在水平方向同时固定了左边距和右边距,那么我们千万不能固定子控件的宽度(反应在storyBoard中的设置,也就是必须使控制子控件宽度的虚线变为实线)。同理, 如果垂直方向同时固定了上边距和下边距,那么我们不能固定子控件的高度(反应在storyBoard中的设置,也就是必须使控制子控件高度的虚线变为实线)。
控制器的view的autoresizing
注意:
如果我们在storyBoard中选中控制器的view,然后在尺寸检查器中查看会发现,autoresizing中控制子控件的宽度和高度的虚线自动变成了实线(然而我并没有点击),这是因为控制器的view的宽高是一个默认值,默认和屏幕的尺寸相等,所以我们不能通过autoresizing来设置控制器的宽高。从另一个角度也能解释:autoresizing是约束子控件和父控件之间的位置关系的,控制器的view并没有父控件,所以不能通过autoresizing来约束控制器的view。
网友评论