Swift iOS:AutoLayout 快速介紹

RecoReco發表於2019-02-20

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的位置上的話,會產生些問題:

  1. 正方形的左上角的x和y的值就得自己計算
  2. 當手機旋轉時,螢幕的橫向長度和縱向長度就變了,因此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用來建立閱讀,其函式引數標籤有:

  1. item 要定位的檢視物件
  2. attribute 要定位的檢視物件的屬性
  3. relatedBy 相對關係,也就是公式內的操作符號,最常用的就是等於,但是也可以使用大於小於等操作符號
  4. toItem 用來對定位點的檢視物件
  5. attribute 用來對定位點的檢視物件的屬性
  6. multiplier 係數
  7. 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的含義。同樣的,正方形的高度也是如此分析。

使用自動佈局,一個檢視的位置的參照物不再僅僅是父檢視的左上腳點,而是變得豐富多彩,依據不同的情況有不同的選擇:

  1. 指定檢視的中心點和父檢視重合
  2. 指定一個檢視和它的兄弟檢視緊靠
  3. 靠左靠右靠上靠下

如此等等。因此之前的指定xy值的做法,就稱為自動佈局方法的一個特殊案例。

相關文章