- 原文地址:What's in your Larder: iOS layout DSLs
- 原文作者:Belle
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:pmwangyang
- 校對者:RickeyBoy
如果你在iOS開發時使用 Auto Layout
來純程式碼佈局的話,你很容易就會感到囉嗦和乏味。DSL(譯者注:原意為「領域特定語言」,在本文中根據語境譯為「佈局語言」)能夠將基礎的API轉換成可以簡單、快速開發和閱讀的程式碼。有很多這類佈局語言支援 Auto Layout,甚至有幾個還支援手動 frame 佈局。
我可以給你推薦一些我偶然遇到並且儲存到我的 Ladar 書籤中的佈局語言(別忘了你可以在 Github 中 star 這些庫,並且可以自動更新到你的 Lardar 賬戶中)。
SnapKit [Swift] & Masonry [Objective-C]
現在我已經使用 SnapKit 一段時間了,在這之前是 Masonry。SnapKit 是 Masonry 的 Swift 版繼承者,二者使用的是同一種構思,都值得使用。
SnapKit 使用閉包(Masonry 則用 block)和點語法來連結自動佈局的約束需求。下面是一個使用 SnapKit 新增約束的例子:
view.addSubview(label)
label.snp.makeConstraints { (make) in
make.label.leading.bottom.equalToSuperview()
make.top.equalTo(anotherView.snp.bottom).offset(12) // Label top == anotherView bottom + 12
make.width.equalToSuperview().dividedBy(2).labeled("label width") // Label width == view width / 2
}
view.snp.makeConstraints { (make) in
make.edges.equalToSuperview() // SnapKit offers some handy shortcuts like .edges and .size to constrain multiple attributes at once
}
複製程式碼
我特別喜歡 SnapKit 的一點是,你可以使用點語法 .labeled()
來命名約束,當你的約束衝突時,可以在 Xcode 中顯示出來。這對找到出問題的約束有很大幫助,以使得你的佈局可以正常工作,但是你不需要因為這個來給每個約束一個唯一的標籤。
EasyPeasy [Swift]
EasyPeasy 提供了一次性新增多個約束的好方法。在使用 SnapKit 時,你可以用 同一種 方法來約束檢視的各個方向,比如:view.leading.trailing.equalToSuperview()
,但是你不能把諸如 leading
和 trailing
這樣不同的約束連結在一起。
在使用 EasyPeasy 時,你可以這樣做:
myView.easy.layout(
Width(200),
Height(120)
)
複製程式碼
EasyPeasy 另一個有趣的地方是可以給約束新增條件,比如:
var isCenterAligned = true
view.easy.layout(
Top(10),
Bottom(10),
Width(250),
Left(10).when { !isCenterAligned },
CenterX(0).when { isCenterAligned }
)
複製程式碼
在 iOS 裡,你也可以使用你正在新增約束的檢視的 UITraitCollection
上下文,更輕鬆地來為不同的裝置和方向調整你的佈局。
Stevia [Swift]
Stevia 是 freshOS 的一部分,而 freshOS 是一個幫助 iOS 開發者在他們的專案中集合庫檔案的專案。Stevia 和 SnapKit 有許多相似點,但有一些不同的地方讓 Stevia 相當吸引人。
其中一個就是 Stevia 提供它自己的視覺化佈局 API。所以,如果你喜歡 Apple VFL 的視覺化效果但是不想那麼囉嗦、不想依賴於字串和字典檢查,Stevia 是一個很好的選擇。這有一個 Stevia 文件中使用視覺化佈局 API 的簡單例子:
layout(
100,
|-email-| ~ 80,
8,
|-password-forgot-| ~ 80,
>=20,
|login| ~ 80,
0
)
複製程式碼
你也可以像 SnapKit 那樣使用 Stevia,比如用點語法連結多個屬性:
email.top(100).left(8).right(8).width(200).height(44)
image.fillContainer()
複製程式碼
你也可以使用等式 API 來佈局,像這樣:
email.Top == 100
password.CenterY == forgot.CenterY
複製程式碼
Stevia 讓我喜歡的一點是,你可以使用像下面這樣的方法同時約束多個檢視:
alignHorizontally(password, forgot)
equalWidths(email, password)
複製程式碼
只需一點點設定,你就可以使用 Stevia 的即時過載功能,讓開發更快捷。
Mortar [Swift]
和 SnapKit 以及 Stevia 不同的是,Mortar 壓根不提供點語法。它有意地避免點語法來提升可讀性。
但不管怎樣,它還是和 Stevia 一樣提供了可視的佈局 API,同樣是一種用程式碼建立自動佈局的簡明的語法。
這是 Mortar 文件中列舉的可視佈局 API 例子:
viewA | viewB[==viewA] || viewC[==40] | 30 | viewD[~~1] | ~~1 | viewE[~~2]
// viewA has a size determined by its intrinsic content size
// viewA is separated from viewB by 0 points (| operator)
// viewB has a size equal to viewA
// viewB is separated from viewC by the default padding (8 points; || operator)
// viewC has a fixed size of 40
// viewC is separated from viewD by a space of 30 points
// viewD has a weighted size of 1
// viewD is separated fom viewE by a weighted space of 1
// viewE has a weighted size of 2
複製程式碼
Mortar 也允許你同時設定多個檢視的約束,這點我認為非常有用。這裡是用不同方式建立 Mortar 約束的概覽:
[view1, view2, view3].m_size |=| (100, 200)
/* Is equivalent to: */
[view1.m_size, view2.m_size, view3.m_size] |=| (100, 200)
/* Is equivalent to: */
view1.m_size |=| (100, 200)
view2.m_size |=| (100, 200)
view3.m_size |=| (100, 200)
複製程式碼
我對 Mortar 大量符號的使用有點失去了興趣,但是符號的運用確實讓語法非常簡潔。我想如果你可以應付這些語法,你可以使用 Mortar 更快、更簡單的新增約束。其他非常好的地方是,Mortar 沒有忽略其他佈局庫並不支援的約束屬性,比如 firstBaseline
。
Bamboo [Swift]
雖然 Bamboo 看起來和我提到的其他庫很相似,但是它確實有一些獨特的地方,值得我們探索。一個讓我喜歡的方面是它的 fill
方法。對於初學者,你可以僅僅使用一個 fill()
來把當前檢視的邊緣貼在父檢視的邊緣上。但是你也可以呼叫比如 fillLeft()
來讓檢視的左、上、下邊緣貼合到父檢視上,或者使用 fillWidth()
來貼合檢視的頭和尾邊緣。
另一組我喜歡的方法是 before()
、after()
、above()
和 below()
。我經常考慮用這種方法定位我的檢視,所以,像這樣用程式碼表達約束和我的思考過程是同步的,這是一個很好的方式。每一個這樣的方法都有一個可選的間隔引數,所以你可以輕鬆地使用間隔引數在一個檢視後面佈局另一個檢視。
給你展示一個我喜歡的特性:你可以使用 Bamboo 同時約束多個檢視:
// Constrain on each item.
// e.g., Set each item's width to 10.
[view1, view2, view3].bb.each {
$0.bb.width(10)
}
// Constrain between every two items.
// e.g., view1.left == view2.left, view2.left == view3.left
[view1, view2, view3].bb.between {
$0.bb.left($1)
}
[view1, view2, view3].bb.left() // align all left
[view1, view2, view3].bb.width(10) // set width of all to 10
複製程式碼
Bamboo 同樣提供一個優雅的選擇,以便你在座標軸上均勻地分佈檢視:
[view1, view2, view3].bb.distributeX(spacing: 10) // [view1]-10-[view2]-10-[view3]
複製程式碼
最後,當自動佈局失效時,Bamboo 也提供了手動 frame 佈局的近似語法。
Cartography [Swift]
Cartography 使用基於閉包的方法來新增約束,每個閉包可以同時約束多個檢視。下面是文件中的例子:
constrain(view1, view2) { view1, view2 in
view1.width == (view1.superview!.width - 50) * 0.5
view2.width == view1.width - 50
view1.height == 40
view2.height == view1.height
view1.centerX == view1.superview!.centerX
view2.centerX == view1.centerX
view1.top >= view1.superview!.top + 20
view2.top == view1.bottom + 20
}
複製程式碼
你也可以一次只約束一個檢視,比如:
constrain(view) { view in
view.width == 100
view.height == 100
}
複製程式碼
你也可以將所有的約束放到一個閉包裡,就像一個 ConstraintGroup
:
let group = constrain(button) { button in
button.width == 100
button.height == 400
}
複製程式碼
或者只在閉包中保留一個約束:
var width: NSLayoutConstraint?
constrain(view) { view in
width = (view.width == 200 ~ 100)
}
複製程式碼
就像本文中其他的佈局語言,Cartography 也提供了 edges
、center
和 size
之類的混合屬性來提高效率。它也提供了簡單的對齊多個檢視的方法:
constrain(view1, view2, view3) { view1, view2, view3 in
align(top: view1, view2, view3)
}
複製程式碼
均勻分佈檢視方面,和 Bamboo 很像:
constrain(view1, view2, view3) { view1, view2, view3 in
distribute(by: 10, horizontally: view1, view2, view3)
}
複製程式碼
其他選擇
你可能還會喜歡下面這些庫:
- LayoutKit [Swift, Objective-C],自動佈局的另一種選擇,LinkedIn開發。
- PinLayout [Swift],一個手動 frame 佈局的佈局語言。
- FlexLayout [Swift],Yoga/Flexbox 的 Swift 版本介面, 由 PinLayout 背後的團隊開發。
- Layout [Swift],使用XML模板檔案佈局的框架。
我還發現了許多的自動佈局語言,但並沒有新增到 Larder 中或在這裡列舉。大部分是因為它們要麼太老並且沒人維護,或者是太簡單、沒有特點。但這並不意味著它們沒有用,只是我在尋找最有趣、最獨特使用自動佈局語法糖的方式罷了。
希望你可以在這個列表裡找到一些新的可嘗試的東西!如果你認為我漏掉了值得探究的庫,請在這個 Twitter @LarderApp 裡和我們分享。
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。