純程式碼 Auto Layout 與概要教學(一)

Denken
4 min readMar 27, 2017

前言

這幾年來工作上寫 iOS app,都是用純程式碼與原生的 Auto Layout。一路上發現網路上純程式碼的寫法教學還是不多,Visual Format Language 更是少,於是將這幾年碰壁學出來的心得,以我理解的方式,整理成這份概要教學。有所疏漏誤解,還請先進不吝指教改正。

預計會有三篇:

本系列文範例,都整理成 Swift Playground 在這專案裡。

從 setFrame 到 Auto Layout constraint

要排版出一個長方形的 UIView,最早的方式是設定 frame:

Coordinate system orientation in UIKit
subview.frame = CGRect(x: 100, y: 50, width: 200, height: 100)

前兩個 (x, y) 設定長方形左上角的原點,後兩個 (width, height) 設定長方形的寬與長,這樣就能唯一決定長方形的樣子。

一般來說是在繼承layoutSubviews()中描述這些 subviews 的關係,畫面有變化時(譬如旋轉)就能即時更新。

然而這樣的方式,在排版更多 subview 時,就很容易需要相當多的算術。

適度配合 autoresizingMask 能緩解這問題,但在不同 subview 排版互相依賴時,依然無法很好地解決(這裡不討論 autoresizingMask,因為後面的 Auto Layout 就能取代)

iOS 6 之後提供另一套更高層次的機制:Auto Layout,它的根本基礎就只有一個 constraint API:

NSLayoutConstraint(item: y, attribute: y.attribute, relatedBy: =, toItem: x, attribute: x.attribute, multiplier: m, constant: c

只用這個 API 就足以完整描述一個 UIView 的樣子。這個 API 是在描述一個線性等式關係:y = m x + c

相較於 setFrame 只有 x, y, width, height 四種描述屬性,Auto Layout 提供了更多的描述屬性(NSLayoutAttribute):

Auto Layout Attributes

而描述這些變數之間的關係,是用線性關係 y = m x + c (m: multiplier, c: constant)(甚至可以用不等式 >=, <= 這裡不討論,因為後面的 VFL 更有機會用到)

直接用實例來解釋這個 API,我們改用 Auto Layout constraint 重做前述例子。假設上層 view 是長這樣:

let view = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))

可以發現前述長方形其實是在置中位置,只需分別設定 centerX, centerY 和 width, height 如下:

由此可以發想,必有其他的描述方式,可以達到一樣的排版效果。

只要 x 方向與 y 方向上,各有 2 個有效給定的 Auto Layout Constraint(等式),就可以唯一決定長方形的樣子。

那麼也可以這樣做,分別設定 top, bottom, left, right 如下:

當然這樣旋轉後的樣子,會與前者不同;所以應該根據 subview 的本質,選擇最合理正確的描述方式。

掌握了 Auto Layout constraint 其實就是描述 y = m x + c 的線性關係之後,很快就能明白,不論是用 Storyboard/Interface Builder 或是 NSLayoutAnchor、PureLayout、Masonry… 等等,其實都是奠基於這一個 API 而已。

以 iOS 9 之後支援的 NSLayoutAnchor 為例,重做前述例子:

P.S. 延伸學習支線:從這篇你可以岔出去自學的題材

  • Auto Layout constraint 可以設定 priority
  • Auto Layout constraint 在 activate 之後,constant 值還可以動態修改!這也是 Auto Layout 架構下做動態效果的基礎
  • 排版某些 UIView 時(譬如 UILabel)可以略去指定其寬度,只描述其他 constraint,因為它們有預設實作 intrinsicContentSize

--

--