AutoLayout
典型的,加入一个子视图的做法就是创建实例,指定位置,然后把它加入到视图里面来。在指定位置的代码处,一般就是设置frame属性即可,就像这样:
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window!.rootViewController = Page()
self.window?.makeKeyAndVisible()
return true
}
}
class Page: UIViewController{
var vw : UIView!
override func viewDidLoad() {
super.viewDidLoad()
vw = UIView()
vw.backgroundColor = .red
vw.frame = CGRect(x: 5, y: 5, width: 50, height: 50)
view.addSubview(vw)
}
}复制代码
代码把一个边长为50的正方形加入到x: 5, y: 5的位置上,位置相对于它的父视图的左上脚点。如果我们的相对点变化了,比如说希望把这个正方形以自身的右下脚点为惨遭,放到相对于父视图的右下脚点的的-5,-5的位置上的话,会产生些问题:
- 正方形的左上角的x和y的值就得自己计算
- 当手机旋转时,屏幕的横向长度和纵向长度就变了,因此x和y的值也得自己重新计算
解决此问题的要点在于,App只是声明自己的位置关系,具体的坐标有UIKit来计算。现在UIKit提供的AutoLayout技术就可以解决此问题。
就以此问题为例,可以去掉:
vw.frame = CGRect(x: 5, y: 5, width: 50, height: 50)复制代码
改成如下的约束:
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window!.rootViewController = Page()
self.window?.makeKeyAndVisible()
return true
}
}
class Page: UIViewController{
var vw : UIView!
override func viewDidLoad() {
super.viewDidLoad()
vw = UIView()
vw.backgroundColor = .red
view.addSubview(vw)
vw.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = NSLayoutConstraint(item: vw, attribute: NSLayoutAttribute.right, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.right, multiplier: 1, constant: -5)
let verticalConstraint = NSLayoutConstraint(item: vw, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: -5)
let widthConstraint = NSLayoutConstraint(item: vw, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 50)
let heightConstraint = NSLayoutConstraint(item: vw, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 50)
view.addConstraints([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
}
}复制代码
首先必须设置此视图的属性:
vw.translatesAutoresizingMaskIntoConstraints = false复制代码
这样AutoLayout才能生效。随后创建的第一约束:
let horizontalConstraint = NSLayoutConstraint(item: vw, attribute: NSLayoutAttribute.right, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.right, multiplier: 1, constant: -5)复制代码
看起来有些复杂,但是如果知道一个公式就更容易读懂此代码。NSLayoutConstraint用来创建阅读,其函数参数标签有:
- item 要定位的视图对象
- attribute 要定位的视图对象的属性
- relatedBy 相对关系,也就是公式内的操作符号,最常用的就是等于,但是也可以使用大于小于等操作符号
- toItem 用来对定位点的视图对象
- attribute 用来对定位点的视图对象的属性
- multiplier 系数
- constant 常数
公式由此标签指定如下:
item.attribute relatedBy toItem.attribute*multiplier + constant复制代码
标签relatedBy指定的是一个操作符号,此处为“=”,因此此公式简化为:
item.attribute = toItem.attribute*multiplier + constant复制代码
再次的把标签带入参数值,就是这样:
vw.right = view.right*1 - 5复制代码
读出来的意思就是:
视图vw的右边x值约束为视图view的右边x值乘以1再减去5复制代码
于是可以类推第二个约束为正方形的下边y值和父视图的下边y值的约束定义:
视图vw的下边y值约束为视图view的下边y值乘以1再减去5复制代码
此案例中,正方形的宽度则只是一个常数值,并不需要相对于任何视图的任何属性,因此创建类NSLayoutConstraint时,公式中:
item.attribute relatedBy toItem.attribute*multiplier + constant复制代码
的toItem.attribute*multiplier整体是无意义的,而简化为:
item.attribute = constant复制代码
这也就是代码:
let widthConstraint = NSLayoutConstraint(item: vw, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 50)复制代码
标签toItem为nil, 标签attribute为NSLayoutAttribute.notAnAttribute的含义。同样的,正方形的高度也是如此分析。
使用自动布局,一个视图的位置的参照物不再仅仅是父视图的左上脚点,而是变得丰富多彩,依据不同的情况有不同的选择:
- 指定视图的中心点和父视图重合
- 指定一个视图和它的兄弟视图紧靠
- 靠左靠右靠上靠下
如此等等。因此之前的指定xy值的做法,就称为自动布局方法的一个特殊案例。