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值的做法,就稱為自動佈局方法的一個特殊案例。