布局简介
Xcode的布局工具共有两套,一套为Autoresizing,另一套为AutoLayout。
Autoresizing
将可视化控件拖放到ViewController上时,默认使用的就是Autoresizing排版。在Storyboard中由两个同心正方形表示,外面的正方形表示SuperView(父视图),里面的正方形代表控件。例如,如果需要控件的宽度随屏幕的宽度自动调整,我们只需要设定控件的「左」、「右」、「上」、以及「水平方向」即可。(可以理解为通过这些线段将控件约束到父视图中,此时他出了宽度之外的位置属性都是被固定的)
AutoLayout
AutoLayout有两个重要的工具,trait variation(特征变量)和constraint(约束)。
- tarit variation(特征变量)帮助我们检查控件在不同屏幕尺寸的设备上的位置是否合理。在Stroyboard中点击「View as: …」按钮可以查看支持预览的设备。点击Vary for Traits可以设置控件是否在符合某种特征的设备中出现,如某些控件只在iPhone直向时出现,横向时不出现。
苹果设备的宽高属性在特征变量中被归纳为regular(正常)和compact(湖紧凑),可以组合出四种不同的状况,如 Regular Width和Compact Height。在Storyboard中通常时以缩写的形式表示(wR和hC)。有时,在iPad布局中布局中会遇到Any,代表在regular和compact之间的情况。 - constraint(约束)帮助我们将控件固定在某个控件或是屏幕的边缘上。当被绑定的控件随着屏幕宽度改变而移动时,绑定到该控件上的控件会随之移动。(tips:在Xcode9之前,以Lable控件为例,如果只设定「左」、「上」约束是没有问题的,在Xcode9之后,会报出警告,因为此时该控件长度过长时可能覆盖其他控件(比如Label中填入了一个超长的字符串或者在多国语言转换时可能会导致字符串超出预想的长度),此时如果让Xcode自动修正,会在控件加上可变动大小的「右」约束(>=)。
Storyboard布局案例
- 在屏幕中放置三个Label控件,水平居中,分别位于屏幕1/4、2/4、3/4处。
首先在Storyboard中放置三个Label控件,勾选Horizontally in Container和Vertically in Container。此时三个控件均在界面中水平垂直居中,此时依次设置三个控件的「Align Center Y」的Multiplier(倍率)为2/4、4/4、6/4。(此时三个Label重叠在一起,可以在Storyboard编辑界面的左侧文件浏览中找到各个Label,设置「Align Center Y」属性的尺寸面板图标为三角形)
需要平均分布的控件的个数与各个控件的倍率对照:
两个 => 2/3,4/3 (1/2 * 2/3 = 1/3, 1/2 * 4/3 = 2/3)
三个 => 2/4,4/4,6/4
四个 => 2/5,4/5,6/5,8/5
…
通过代码布局
- iOS原生的使用constraint进行布局的方式共有三种,一种时在Storyboard上用视觉化的方式设定,不需要写代码;第二种通过NSLayoutConstraint类,通过代码来编写;第三种使用NSLayoutConstraint类的专属语法(VFL)通过代码来控制约束。(第三种语法很复杂,不建议使用。了解即可,通常使用前两种或通过Snapkit第三方框架等来进行布局)
在设置控件的约束之前需要先关闭控件的autoresizing属性,然后调用addSubView()将控件加到父视图中。
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubView(label)
接下来就可以设置控件的约束了,这里以「高度」约束举例
label.heightAnchor.constraint(equalToConstant: 21).isActive = true
或者按如下格式
NSLayoutConstraint.activate([
label.heightAnchor.constraint(equalToConstant: 5),
label.widthAnchor.constraint(equalToConstant: 21),
])
注意设置「右」约束时的值通常是负数。
进行布局时获取当前设备种类
- 通过traitCollection属性判断当前设备种类
switch traitCollection.userInterfaceIdiom {
case .pad:
print("此设备为iPad")
case .phone:
print("此设备为iPhone")
case .tv:
print("此设备为Apple TV")
case .carPlay:
print("此设备为车用设备")
case .unspecified:
print("无法判别")
}
- 当特征变量发生变化(长宽的regular或者compact)时会调用willTransistion(to:with:)函数。(iPad的width和height都被Apple设定为regular,因此当iPad的方向有变化时,这个函数不会被调用)
- 当设备宽高分辨率发生变化时会调用viewWilltransition(to:with:)函数,在函数中可以通过UIDevice来判断设备是横向的还是直向的。
switch UIDevice.current.orientation {
case .faceDown:
print("设备面朝下")
case .faceUp:
print("设备面朝上")
case .landscapeLeft:
print("设备横向,HOME在右边")
case .landscapeRight:
print("设备横向,HOME在左边")
case .portrait:
print("设备直向")
case .portraitUpsideDown:
print("设备上下颠倒")
case .unknown:
print("无法判定")
}
使用StackView控件布局
StackView是一个专门用于布局的控件,先将界面分割出所需要的区域,然后将各个控件放置到对应的区域中。通过StackView的各种属性,以及给各控件添加约束来完成各种复杂的布局。StackView控件有水平和垂直方向两种类型,由于布局时使用频繁,Xcode直接提供了这两种不同方向的StackView。StackView控件也可以内嵌StackView控件形成多样化的布局。
- 在一个垂直方向的StackView中放入一个ImageView和一个TextView,给ImageView加上大小为100的「高度」约束,剩下的控件就全部分配给TextView了。