前言
在上篇文章中冕象,我們初步學(xué)習(xí)了SnapKit
的基礎(chǔ)使用方法荠诬,文章:Swift自動布局SnapKit的詳細(xì)使用介紹。一般來說焙蹭,掌握了那些基本方法的使用晒杈,基本上在項目中布局就沒多大問題了,你基本可以達(dá)到自己想要的效果孔厉。但是拯钻,技術(shù)這東西,永遠(yuǎn)都不嫌知道的多撰豺,嫌多只能證明你懂得真的太少了粪般。閑言一句,在寫上篇文章的時候郑趁,SnapKit
的stars才9340顆星刊驴,而現(xiàn)在,已經(jīng)達(dá)到11608顆星寡润,足足增長了2000多顆星捆憎,歷時大概7個月GitHub。另外梭纹,SnapKit
也升級到了4.0版本躲惰,在布局上有些許改進(jìn),比如適配了最新的裝逼神器iPhone X的safeAreaLayoutGuide
, 極大的方便我們的開發(fā)工作变抽。
回顧
- 創(chuàng)建一個視圖础拨,大小為100, 居中父視圖绍载?
在前一篇文章中诡宗,我們也介紹過怎么去布局一個視圖,包括大小和位置击儡,事實上塔沃,有時候采用更加便利的方法是及其明智的,我們可以寫成下面這樣
class ViewController: UIViewController {
lazy var redView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
redView.backgroundColor = UIColor.red
view.addSubview(redView)
redView.snp.makeConstraints { (make) in
// 寬高相等時阳谍,我們直接寫把size寫成一遍就行蛀柴,即 width = height = 100
make.size.equalTo(100)
make.center.equalToSuperview()
}
}
}
這樣螃概,一個簡單的布局就完成了:
- SnapKit的 dividedBy、multipliedBy鸽疾、offset树姨、inset 使用
首先來看一下源代碼截圖:
在上篇文章中焚挠,我們沒細(xì)講dividedBy
尺栖、multipliedBy
碳胳,主要是我?guī)缀踉陂_發(fā)中不用它,其實很多人應(yīng)該都很少用到它弄企,在特殊情況下或許也能用得到
比如看一下 dividedBy
class ViewController: UIViewController {
lazy var redView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
redView.backgroundColor = UIColor.red
view.addSubview(redView)
redView.snp.makeConstraints { (make) in
// width 是父視圖寬的一半
make.width.equalToSuperview().dividedBy(2)
// height 是父視圖高的三分之一
make.height.equalToSuperview().dividedBy(3)
make.center.equalToSuperview()
}
}
}
multipliedBy
也是同樣的道理:
class ViewController: UIViewController {
lazy var redView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
let greenView = UIView()
greenView.backgroundColor = .green
view.addSubview(greenView)
greenView.snp.makeConstraints { (make) in
// 距離父視圖頂部64 (一會我們會講到 iPhone X 的安全區(qū)域布局)
make.top.equalTo(64)
// 設(shè)置視圖大小
make.size.equalTo(CGSize(width: 20, height: 30))
// x軸方向上居中
make.centerX.equalToSuperview()
}
redView.backgroundColor = UIColor.red
view.addSubview(redView)
redView.snp.makeConstraints { (make) in
// width 是綠色視圖寬的10倍
make.width.equalTo(greenView).multipliedBy(10)
// height 是綠色視圖高的5倍
make.height.equalTo(greenView).multipliedBy(5)
make.center.equalToSuperview()
}
}
}
至于offset
(偏移量超燃,也可以叫位移)、inset
(插入)其實在之前我們也提到過了拘领,用法也比較容易意乓,我們再來溫故一下
class ViewController: UIViewController {
lazy var redView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
redView.backgroundColor = UIColor.red
view.addSubview(redView)
redView.snp.makeConstraints { (make) in
// 距離(偏移)父視圖頂部 64
make.top.equalToSuperview().offset(64)
// 距離(偏移)父視圖左邊 150
make.left.equalToSuperview().offset(150)
// 距離(偏移)父視圖右邊 150
make.right.equalToSuperview().offset(-150)
// 距離(偏移)父視圖底部 500
make.bottom.equalToSuperview().offset(-500)
}
}
}
對于上面這個例子,我們使用 inset
來代替offset
的寫法约素,將會更加簡潔明了:
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 64, left: 150, bottom: 500, right: 150))
上面例子說明届良,布局還需要取巧,一行代碼就能搞定的就不要寫兩行代碼圣猎,不要蠻橫布局士葫,要學(xué)會化復(fù)雜為簡單,化腐朽為神奇送悔,寫出詩一般的代碼慢显。
進(jìn)階
在“進(jìn)階”前,首先我們會先看一下一個問題欠啤,看什么問題呢荚藻?
在上一篇文章中,我曾經(jīng)說過洁段,在SnapKit庫里面应狱,有幾個容易弄混淆,令部分使用該庫的開發(fā)者百思不得其姐的問題祠丝,當(dāng)時我也是其中之一的迷惑者疾呻。
在布局的時候,我們經(jīng)常會看到這樣幾個屬性:
equalTo()
lessThanOrEqualTo()
greaterThanOrEqualTo()
它們之間到底有什么區(qū)別写半?有什么樣的作用岸蜗?在上篇文章中,我說一般情況下我們只需要使用equalTo()
就行了叠蝇,似乎lessThanOrEqualTo()
和 greaterThanOrEqualTo()
并沒有多大的作用璃岳,甚至沒有作用。好了,為了弄清楚這個疑惑矾睦,我向作者@robertjpayne做了提問, 而他的回復(fù)是
this is managed by Auto Layout from Apple, equalTo will always force a constraint to be
exactly equal to. greaterThanOrEqualTo will allow the constraint to be at least that value
or higher, this can happen when you have competing constraints that would otherwise conflict.
什么意思呢炎功?
作者表明
這是由蘋果的自動布局機制管理枚冗,equalTo()始終強制一個約束恰好等于某一個特定值,由用戶來控制蛇损。而對于
greaterThanOrEqualTo 來說赁温,它允許這個約束至少為那個值或者更大的值,這會發(fā)生的情況是你的布局約束
互相之間有沖突的時候淤齐。
從作者的話也很好簡單理解股囊,也就是當(dāng)你的布局存在沖突或者矛盾的時候,而你恰好使用了lessThanOrEqualTo()
或者greaterThanOrEqualTo()
的時候更啄,蘋果的Auto Layout
會在適當(dāng)?shù)臅r候給你補齊約束或者可以說優(yōu)化約束稚疹,使你的布局不至于顯示錯誤或者甚至導(dǎo)致程序奔潰。
為了更好說明這個問題祭务,我們舉一個例子
class ViewController: UIViewController {
lazy var orangeView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
orangeView.backgroundColor = .orange
view.addSubview(orangeView)
orangeView.snp.makeConstraints { (make) in
// width >= 200
make.width.greaterThanOrEqualTo(200)
// width < 100
make.width.lessThanOrEqualTo(100)
make.height.equalTo(100)
make.center.equalToSuperview()
}
}
}
好内狗,視圖幾乎如愿以償?shù)仫@示在屏幕上,但是我們來看一下輸出log:
恩义锥?布局成功柳沙,視圖顯示正確。日志輸出警告拌倍,回過頭看代碼赂鲤,哪里不對
// width >= 200
make.width.greaterThanOrEqualTo(200)
// width < 100
make.width.lessThanOrEqualTo(100)
顯然是寬度約束存在矛盾或沖突,既想要寬度至少為200柱恤,又要寬度小于100数初,告訴我,你小學(xué)數(shù)學(xué)老師是誰膨更?
但是妙真,為什么布局如此之惡劣卻又顯示出來了呢?好荚守,這就是作者所說的蘋果自動布局機制管理了珍德,當(dāng)我們約束沖突不確定的時候,Apple會根據(jù)適當(dāng)管理一些布局矗漾,推敲哪個約束更為合理锈候,忽略不合理的約束。
盡管如此敞贡,但是我個人還是提倡使用明確的布局約束泵琳,這會讓我們很確定,我們想要做什么?正在做什么获列?這樣做是否正確谷市?能不能達(dá)到我預(yù)期的效果?
SnapKit進(jìn)階之 topLayoutGuide 和 bottomLayoutGuide
這兩個是UIViewController的屬性击孩,之所以要放在這里講迫悠,是因為在SnapKit
中,我們也可以使用這兩個屬性進(jìn)行布局巩梢,至于怎么樣布局?我會詳細(xì)地給大家說明创泄。但是必須要提的是,這兩個屬性在iOS 11的時候已經(jīng)被廢棄了括蝠,有興趣者可以在你的Xcode中點進(jìn)去看一下鞠抑,而廢棄的原因是:在iPhone X
出來之后,系統(tǒng)也隨之升級忌警,增加了安全區(qū)域(SafeArea)的概念搁拙,我們一會還會繼續(xù)講到這個問題, 先來看 topLayoutGuide
法绵、bottomLayoutGuide
topLayoutGuide
代碼:
class ViewController: UIViewController {
lazy var orangeView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
orangeView.backgroundColor = .orange
view.addSubview(orangeView)
orangeView.snp.makeConstraints { (make) in
make.top.equalTo(topLayoutGuide.snp.top) // 注意這里感混,topLayoutGuide的頂部
make.left.equalTo(10)
make.right.equalTo(-250)
make.bottom.equalTo(bottomLayoutGuide.snp.bottom)
}
let blueView = UIView()
blueView.backgroundColor = .blue
view.addSubview(blueView)
blueView.snp.makeConstraints { (make) in
make.top.equalTo(topLayoutGuide.snp.bottom) // 注意這里,topLayoutGuide的底部
make.right.equalTo(-10)
make.left.equalTo(250)
make.bottom.equalTo(bottomLayoutGuide.snp.bottom)
}
}
}
在iPhone X
之前的設(shè)備礼烈,topLayoutGuide
代表的是狀態(tài)欄(status bar)區(qū)域弧满,也就高度是 20dp
. 然而,iPhone X
中此熬,topLayoutGuide
代表的也是類似狀態(tài)欄所在的區(qū)域庭呜,不過在iPhone X
中,這個高度是 44dp
, 可能有人已經(jīng)注意到了犀忱,是的募谎,就是一個導(dǎo)航欄的高度,只不過阴汇,和iPhone X之前所謂的導(dǎo)航欄(64dp)相比数冬,少了一個狀態(tài)欄的區(qū)域高度,這是iPhone X所帶來的安全區(qū)域導(dǎo)致搀庶」丈矗看下面上下截圖中的紅線和綠線這個區(qū)域 (注:橙色視圖的頂部
是topLayoutGuide的頂部
,藍(lán)色視圖的頂部
是topLayoutGuide的底部
)
bottomLayoutGuide
代碼:
class ViewController: UIViewController {
lazy var orangeView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
orangeView.backgroundColor = .orange
view.addSubview(orangeView)
orangeView.snp.makeConstraints { (make) in
make.top.equalTo(topLayoutGuide.snp.top)
make.left.equalTo(10)
make.right.equalTo(-250)
make.bottom.equalTo(bottomLayoutGuide.snp.top) // 注意這里哥倔,bottomLayoutGuide的頂部
}
let blueView = UIView()
blueView.backgroundColor = .blue
view.addSubview(blueView)
blueView.snp.makeConstraints { (make) in
make.top.equalTo(topLayoutGuide.snp.top)
make.right.equalTo(-10)
make.left.equalTo(250)
make.bottom.equalTo(bottomLayoutGuide.snp.bottom) // 注意這里秸架,bottomLayoutGuide的底部
}
}
}
既然在iPhone X和iPhone X之前的設(shè)備上,topLayoutGuide
是有差異的咆蒿,那么东抹,對于bottomLayoutGuide
呢蚂子?我們繼續(xù)來看一下在這個裝逼神器出來前后的一個對比(注:橙色視圖的底部
是bottomLayoutGuide的頂部
,藍(lán)色視圖的底部
是bottomLayoutGuide的底部
)
|
從上面對比可以看出缭黔,對于bottomLayoutGuide
來說食茎,在iPhone X之前的設(shè)備,它的頂部和底部都是代表在當(dāng)前控制器View
的底部馏谨; 而對于iPhone X來說董瞻,它的底部代表在當(dāng)前控制器View
的底部, 但是它的頂部不在和底部一樣在當(dāng)前控制器View
的底部田巴,而是向上偏移了一段距離,這段距離為 34dp
, 恩挟秤?為啥會多出這么一個高度呢壹哺?其實這還是和iPhone X所涉及的安全區(qū)域有著本質(zhì)的聯(lián)系。
小結(jié):SnapKit可以使用
topLayoutGuide
和bottomLayoutGuide
對視圖進(jìn)行布局艘刚,并且在iPhone X
設(shè)備上可以避開所謂的安全區(qū)域
管宵,但是要提醒的是,這兩個屬性在iOS 11
已經(jīng)被廢棄了攀甚,盡管現(xiàn)在還可以使用箩朴,但也是兼容而已,后期逐漸會被直接從API中移除秋度,所以只是作為了解,不推薦使用荚斯。
問題來了埠居,既然不推薦使用該屬性,那有使用什么呢事期?好的滥壕,隨著Apple
的海哥(iPhone X
)的到來,我們可以使用新特性safeAreaLayoutGuide
來代替topLayoutGuide
和bottomLayoutGuide
布局兽泣,在SnapKit中同樣可以用來自動布局绎橘,這是SnapKit升級到4.0
的時候更新兼容的,所以唠倦,對于使用SnapKit4.0
一下版本的伙伴称鳞,如有需要,請自行升級使用稠鼻。
下面我們將進(jìn)入使用SnapKit對 iPhone X
的安全區(qū)域的自動布局了胡岔。看到這里累了嗎枷餐?好的靶瘸,眼神疲勞苫亦,我們來活動一下
SnapKit進(jìn)階之 safeAreaLayoutGuide
safeAreaLayoutGuide 的概念
safeAreaLayoutGuide
, 安全區(qū)域布局對象或?qū)傩裕?strong>iOS 11開始被引入怨咪,主要是用于日常開發(fā)中自動布局屋剑。必須注意的是,之前我們說的topLayoutGuide 和 bottomLayoutGuide
是ViewController
的屬性诗眨,但是這里的safeAreaLayoutGuide
不再是ViewController
的屬性唉匾,而是View
的屬性。對于我個人來說匠楚,我覺得這是一個很合理的改變巍膘,畢竟,我們布局針對的是視圖芋簿,而非控制器峡懈,那對于布局對象或者屬性,應(yīng)該被視圖所持有与斤。
代碼:
class ViewController: UIViewController {
lazy var magentaView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
magentaView.backgroundColor = .magenta
view.addSubview(magentaView)
magentaView.snp.makeConstraints { (make) in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top)
make.left.right.equalToSuperview()
make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom)
}
}
}
結(jié)論如下:
所謂安全區(qū)域肪康,就是截圖中
品紅色區(qū)域
在iPhone X之前, view.safeAreaLayoutGuide(即安全區(qū)域)的頂部距離當(dāng)前窗口頂部一個狀態(tài)欄的高度(20dp)
對于iPhone X來說撩穿,view.safeAreaLayoutGuide(即安全區(qū)域)的頂部距離當(dāng)前窗口頂部一個導(dǎo)航欄的高度(44dp)
在iPhone X之前磷支, view.safeAreaLayoutGuide(即安全區(qū)域)的底部距離就是當(dāng)前窗口(當(dāng)前視圖)的底部
對于iPhone X來說,view.safeAreaLayoutGuide(即安全區(qū)域)的底部距離當(dāng)前窗口底部 34dp
安全區(qū)域小結(jié):
iPhone X在以前設(shè)備的基礎(chǔ)上食寡,頂部使?fàn)顟B(tài)欄增加了24dp的高度雾狈,底部直接強制增加了34dp的高度,如果在適配iPhone X上抵皱,我們除了可以使用SnapKit通過view.safeAreaLayoutGuide進(jìn)行自動布局箍邮,還可以根據(jù)狀態(tài)欄(status bar)的高度判斷是否大于20來設(shè)置相應(yīng)的高度,保證當(dāng)前視圖的布局在安全區(qū)域之內(nèi)叨叙。
補充
Snap的優(yōu)先級
在上篇文章中锭弊,我們說過,SnapKit可以通過屬性priority
來設(shè)置優(yōu)先級擂错,當(dāng)時我們是通過具體的數(shù)字來設(shè)置優(yōu)先級的味滞,事實上,SnapKit有一個內(nèi)置的優(yōu)先級钮呀,它是一個結(jié)構(gòu)體剑鞍,在優(yōu)先級不是需要設(shè)置很多的時候,我們完全可以使用SnapKit內(nèi)置的便利方法來進(jìn)行優(yōu)先級的設(shè)置
首先我們來看一下內(nèi)置優(yōu)先級的源碼
很明顯爽醋,優(yōu)先級順序是:required > high > medium > low
例子:
class ViewController: UIViewController {
lazy var brownView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
brownView.backgroundColor = .brown
view.addSubview(brownView)
brownView.snp.makeConstraints { (make) in
// 設(shè)置優(yōu)先級的4中內(nèi)置快捷方式
make.width.equalTo(50).priority(.required)
make.width.equalTo(100).priority(.high)
make.width.equalTo(200).priority(.medium)
make.width.equalTo(300).priority(.low)
make.height.equalTo(250)
make.center.equalToSuperview()
}
}
}
分別設(shè)置了4個優(yōu)先級蚁署,但是required的優(yōu)先級最高,所以所布局的視圖的width為50蚂四。
總結(jié)
SnapKit受歡迎程度越來越高光戈,包括我自己也很喜歡使用其來自動布局哪痰,屏幕適配,這給我們帶來了很大的方便久妆。另外晌杰,SnapKit作者也一直在維護這個庫,由于Swift
現(xiàn)在并非完全成熟和穩(wěn)定筷弦,版本的升級肋演,API的迭代更改都勢必會造成開發(fā)的困擾,開心和感恩的是烂琴,改庫一直被熱情的開發(fā)者所維護爹殊,保證我們在新舊項目中都可以版本兼容。下一章奸绷,有時間的話梗夸,我們會一起走進(jìn)SnapKit
的源碼世界,在那里健盒,我們將了解和學(xué)習(xí)這庫的偉大之處。
歡迎加入 iOS(swift)開發(fā)互助群:QQ群號: 558179558称簿, 相互討論和學(xué)習(xí)扣癣!