[心得] 純程式碼 Auto Layout(二): VFL

看板MacDev作者 (Denken)時間8年前 (2017/04/04 12:21), 編輯推噓3(300)
留言3則, 3人參與, 最新討論串1/1
這是「純程式碼 Auto Layout 與概要教學」系列第二篇。總共會有三篇: * 從 setFrame 到 Auto Layout constraint * Visual Format Language (VFL) * 何時需要 UIStackView? 本系列文範例,都整理成 Swift Playground 在這專案裡 https://github.com/denkeni/Auto-Layout-Programmatically (由於批踢踢不方便貼程式碼,會精簡摘錄 含完整程式碼與後續文章更新,請到網頁版 https://goo.gl/AWOCI2 ) # 前言 純程式碼建立 Auto Layout constraint 在 iOS 6 剛推出時只有兩種方式 其一是前篇文所述的線性關係 其二即是 Visual Format Language (VFL) 到 iOS 9 之後才有第三種方式 NSLayoutAnchor 然而蘋果推出 Auto Layout 至今 幾乎所有官方文件都推廣透過 Interface Builder 來設定 constraint 這使得長期以來官方文件僅短短一頁的 VFL 成為被束之高閣的瑰寶 # Visual Format Language (VFL) VFL 可讀性不錯,尤其在相當複雜程度的排版更是如此, 因為 VFL 的根本基礎,就只有一段即見即所得的字串: ``` NSLayoutConstraint.constraints(withVisualFormat: VFLString, options: NSLayoutFormatOptions, metrics: [String : Any], views: [String : Any]) ``` 直接用實例來解釋這個 API 我們改用 Auto Layout VFL 重做前篇例子 subviewB 的排版方式 分別設定 top, bottom, left, right 如下: https://cdn-images-1.medium.com/max/800/1*PZarxIPo6anPxjm8VysOhw.png
```精簡版 VisualFormat: "V:|-(150)-[subviewB]-(150)-|" // V: Vertical VisualFormat: "H:|-(100)-[subviewB]-(100)-|" // H: Horizontal ``` 首先可以發現,VFL 其實就是自動幫我們生成多個 constraints 所以改用 += 來加入 constraints array 以中括號描述 view 以小括號描述數值 座標軸同樣是由上往下(V)、由左向右(H)的方向描述 直線 `|` 表示為 superview `V:|-(150)-[subviewB] `表示為 subviewB 與其 superview 的 top 相距 150pt `V:[subviewB]-(150)-|` 表示為 subviewB 與其 superview 的 bottom 相距 150pt 兩者合併起來就是相當直觀的 `V:|-(150)-[subviewB]-(150)-|` 多數人不使用 options 而採用預設的空值 [] metrics 是用來描述 VFL 字串中「數值常數」的對應字典 views 則是用來描述 VFL 字串中「view 變數」的對應字典 故前例也可以修改成這樣: ```精簡版 let metrics = ["v": 150, "h": 100] VisualFormat: "V:|-(v)-[subview]-(v)-|" VisualFormat: "H:|-(h)-[subview]-(h)-|" ``` 由此也會發現 只用 VFL 無法描述前篇例子 subviewA, subviewC 的排版方式 因為 VFL 無法描述 centerX, centerY 因此,在許多情況下 VFL 還是得合併使用 constraint 來正確描述 subview 我們改用 Auto Layout VFL + NSLayoutAnchor 重做前篇例子 subviewA, subviewC 的排版方式 分別設定 centerX, centerY 和 width, height 如下: ```精簡版 subviewA.centerXAnchor.constraint(equalTo: view.centerXAnchor) subviewA.centerYAnchor.constraint(equalTo: view.centerYAnchor) VisualFormat: "H:[subviewA(200)]" // width VisualFormat: "V:[subviewA(100)]" // height ``` 基於 VFL,也可以很容易地描述較為複雜的排版了: https://cdn-images-1.medium.com/max/800/1*_7pEUtg78EELsPmSXpL2UQ.png
```精簡版 let metrics = ["p": 15] // padding VisualFormat: "H:|-(p)-[subview1(100)]-(10)-[subview2(120)]-(10)-[subview3]-(p)-|" "V:|-(p)-[subview1]-(p)-|" "V:|-(p)-[subview2]-(p)-|" "V:|-(p)-[subview3]-(p)-|" ``` 當然,在描述更為複雜的二維排版時 由於一段 VFL 只能描述一維排版關係 經常需要組合多段 VFL 才能完整而正確地描述排版 # 番外篇:NSLayoutFormatOptions 這個選項可在描述 X 方向 VFL 時,提供 Y 方向排版的準則 反之亦然。 以下述為例: https://cdn-images-1.medium.com/max/800/1*pyc3FixPF8775DPTkQTnVQ.png
```精簡版 VisualFormat: "H:|-(15)-[subview1(100)]-(10)-[subview2(120)]-(10)-[subview3]-(15)-|", options: .alignAllTop, metrics: nil, views: viewsDict) VisualFormat: "V:[subview1(300)]" VisualFormat: "V:[subview2(200)]" VisualFormat: "V:[subview3(150)]" ``` # VFL Coding Style 以下是我自己偏好的 VFL Coding Style 原則,並藉此解釋 VFL 的相容語法: * view 間距常數值加上括號 * view 間距常數值為等式時,不加上 `==` ex. `[view1]-(15)-[view2]` 而非 `[view1]-15-[view2]` 或 `[view1]-(==15)-[view2]` 這也是考量了使用不等式時,必須加上括號才能運作 ex. `[view1]-(>=15)-[view2]` * view 間距常數值為 0 時,依然要標示出來 ex. `[view1]-(0)-[view2]` 而非 `[view1][view2]` ex. `|-(0)-[view]-(0)-|` 而非 `|[view]|` * 不使用預設的間距 ex. `[view1]-(8)-[view2]` 而非 `[view1]-[view2]`(兩者效果相同) P.S. 有一專案 [Swiftstraints](https://github.com/Skyvive/Swiftstraints) 將 VFL 精簡成僅需一段字串,字串內可直接取用變數, 便可省略了 metrics 與 views 參數。 -- 我最近寫了一個叫作「工作咖啡館」的 iOS App https://goo.gl/iBWJSs 我朋友拖拖拉拉也生出了 Android 版叫作「CaffeeTrip」 https://goo.gl/HnUQWQ -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 219.85.169.16 ※ 文章網址: https://www.ptt.cc/bbs/MacDev/M.1491279670.A.17A.html

04/06 22:52, , 1F
04/06 22:52, 1F

05/04 17:56, , 2F
05/04 17:56, 2F

05/13 00:09, , 3F
05/13 00:09, 3F
文章代碼(AID): #1Ounys5w (MacDev)
文章代碼(AID): #1Ounys5w (MacDev)