Swift自動布局SnapKit的進(jìn)階篇

前言

在上篇文章中冕象,我們初步學(xué)習(xí)了SnapKit的基礎(chǔ)使用方法荠诬,文章:Swift自動布局SnapKit的詳細(xì)使用介紹。一般來說焙蹭,掌握了那些基本方法的使用晒杈,基本上在項目中布局就沒多大問題了,你基本可以達(dá)到自己想要的效果孔厉。但是拯钻,技術(shù)這東西,永遠(yuǎn)都不嫌知道的多撰豺,嫌多只能證明你懂得真的太少了粪般。閑言一句,在寫上篇文章的時候郑趁,SnapKitstars9340顆星刊驴,而現(xiàn)在,已經(jīng)達(dá)到11608顆星寡润,足足增長了2000多顆星捆憎,歷時大概7個月GitHub。另外梭纹,SnapKit也升級到了4.0版本躲惰,在布局上有些許改進(jìn),比如適配了最新的裝逼神器iPhone XsafeAreaLayoutGuide, 極大的方便我們的開發(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()
        }
        
    }
}

這樣螃概,一個簡單的布局就完成了:

photo
  • SnapKit的 dividedBy、multipliedBy鸽疾、offset树姨、inset 使用

首先來看一下源代碼截圖:

photo

在上篇文章中焚挠,我們沒細(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()
        }
        
    }
}
photo

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()
        }
        
    }
}
photo

至于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)
        }
        
        
    }
}
photo

對于上面這個例子,我們使用 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ù)是

picture
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()
        }
        
    }
}
photo

好内狗,視圖幾乎如愿以償?shù)仫@示在屏幕上,但是我們來看一下輸出log

picture

恩义锥?布局成功柳沙,視圖顯示正確。日志輸出警告拌倍,回過頭看代碼赂鲤,哪里不對

// 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的底部

iPhone X 之前的設(shè)備

|
iPhone X
  • 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 XiPhone X之前的設(shè)備上,topLayoutGuide是有差異的咆蒿,那么东抹,對于bottomLayoutGuide呢蚂子?我們繼續(xù)來看一下在這個裝逼神器出來前后的一個對比(注:橙色視圖的底部bottomLayoutGuide的頂部,藍(lán)色視圖的底部bottomLayoutGuide的底部

iPhone X 之前的設(shè)備

|
iPhone X

從上面對比可以看出缭黔,對于bottomLayoutGuide來說食茎,在iPhone X之前的設(shè)備,它的頂部和底部都是代表在當(dāng)前控制器View底部馏谨; 而對于iPhone X來說董瞻,它的底部代表在當(dāng)前控制器View底部, 但是它的頂部不在和底部一樣在當(dāng)前控制器View的底部田巴,而是向上偏移了一段距離,這段距離為 34dp, 恩挟秤?為啥會多出這么一個高度呢壹哺?其實這還是和iPhone X所涉及的安全區(qū)域有著本質(zhì)的聯(lián)系。

小結(jié):SnapKit可以使用topLayoutGuidebottomLayoutGuide對視圖進(jìn)行布局艘刚,并且在iPhone X設(shè)備上可以避開所謂的安全區(qū)域管宵,但是要提醒的是,這兩個屬性在iOS 11已經(jīng)被廢棄了攀甚,盡管現(xiàn)在還可以使用箩朴,但也是兼容而已,后期逐漸會被直接從API中移除秋度,所以只是作為了解,不推薦使用荚斯。

問題來了埠居,既然不推薦使用該屬性,那有使用什么呢事期?好的滥壕,隨著Apple海哥iPhone X)的到來,我們可以使用新特性safeAreaLayoutGuide來代替topLayoutGuidebottomLayoutGuide布局兽泣,在SnapKit中同樣可以用來自動布局绎橘,這是SnapKit升級到4.0的時候更新兼容的,所以唠倦,對于使用SnapKit4.0一下版本的伙伴称鳞,如有需要,請自行升級使用稠鼻。

下面我們將進(jìn)入使用SnapKitiPhone X的安全區(qū)域的自動布局了胡岔。看到這里累了嗎枷餐?好的靶瘸,眼神疲勞苫亦,我們來活動一下

girl

SnapKit進(jìn)階之 safeAreaLayoutGuide

safeAreaLayoutGuide 的概念

safeAreaLayoutGuide, 安全區(qū)域布局對象或?qū)傩裕?strong>iOS 11開始被引入怨咪,主要是用于日常開發(fā)中自動布局屋剑。必須注意的是,之前我們說的topLayoutGuide 和 bottomLayoutGuideViewController的屬性诗眨,但是這里的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)
        }
        
    }
}
iPhone X 之前的設(shè)備

|
iPhone X

結(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()
        }
        
    }
}
photo

分別設(shè)置了4個優(yōu)先級蚁署,但是required的優(yōu)先級最高,所以所布局的視圖的width50蚂四。

總結(jié)

SnapKit受歡迎程度越來越高光戈,包括我自己也很喜歡使用其來自動布局哪痰,屏幕適配,這給我們帶來了很大的方便久妆。另外晌杰,SnapKit作者也一直在維護這個庫,由于Swift現(xiàn)在并非完全成熟和穩(wěn)定筷弦,版本的升級肋演,API的迭代更改都勢必會造成開發(fā)的困擾,開心和感恩的是烂琴,改庫一直被熱情的開發(fā)者所維護爹殊,保證我們在新舊項目中都可以版本兼容。下一章奸绷,有時間的話梗夸,我們會一起走進(jìn)SnapKit的源碼世界,在那里健盒,我們將了解和學(xué)習(xí)這庫的偉大之處。

歡迎加入 iOS(swift)開發(fā)互助群:QQ群號: 558179558称簿, 相互討論和學(xué)習(xí)扣癣!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市憨降,隨后出現(xiàn)的幾起案子父虑,更是在濱河造成了極大的恐慌,老刑警劉巖授药,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件士嚎,死亡現(xiàn)場離奇詭異,居然都是意外死亡悔叽,警方通過查閱死者的電腦和手機莱衩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娇澎,“玉大人笨蚁,你說我怎么就攤上這事√俗” “怎么了括细?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長戚啥。 經(jīng)常有香客問我奋单,道長,這世上最難降的妖魔是什么猫十? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任览濒,我火速辦了婚禮呆盖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匾七。我一直安慰自己絮短,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布昨忆。 她就那樣靜靜地躺著丁频,像睡著了一般。 火紅的嫁衣襯著肌膚如雪邑贴。 梳的紋絲不亂的頭發(fā)上席里,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機與錄音拢驾,去河邊找鬼奖磁。 笑死,一個胖子當(dāng)著我的面吹牛繁疤,可吹牛的內(nèi)容都是我干的咖为。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼稠腊,長吁一口氣:“原來是場噩夢啊……” “哼躁染!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起架忌,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤吞彤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后叹放,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饰恕,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年井仰,在試婚紗的時候發(fā)現(xiàn)自己被綠了埋嵌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡俱恶,死狀恐怖莉恼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情速那,我是刑警寧澤俐银,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站端仰,受9級特大地震影響捶惜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜荔烧,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一吱七、第九天 我趴在偏房一處隱蔽的房頂上張望汽久。 院中可真熱鬧,春花似錦踊餐、人聲如沸景醇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽三痰。三九已至,卻和暖如春窜管,著一層夾襖步出監(jiān)牢的瞬間散劫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工幕帆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留获搏,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓失乾,卻偏偏與公主長得像常熙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子碱茁,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容