AutoLayout 使用詳解

前言


故事從一年前提及,當時因爲接到一個新項目開發任務開發以前想了想之前項目UI佈局方式大多數都是frame計算有的也用到masonry
frame你們都知道適配各類屏幕很是繁瑣各類座標size計算代碼很冗餘後期難以維護。
masonry開源給iOS開發者帶來福音簡化了AutoLayout使用方式,可是我以爲masonry還不足夠快捷方便(有的api不知道什麼意思學習成本比較高),尤爲是動態佈局masonry更新約束至關不方便,後來就決定本身開發AutoLayout庫也就是今天WHC_AutoLayoutKitjavascript

在閱讀以前能夠先看看例子項目:github.com/netyouli/WH… java

簡介


  • API採用鏈式調用(快捷方便)一行代碼搞定佈局
  • 提供【Objective-C】【Swift2.3】【Swift3.0】三種語言版本庫
  • 包含一行代碼計算UITableViewCell高度模塊帶緩存高度
  • 包含WHC_StackView模塊(目的替代系統UIStackView)
  • 隱式更新約束技術(核心後面重點介紹)

鏈式調用


view.whc_Left(10)      //view與父視圖左邊距10
    .whc_Right(10)     //view與父視圖右邊距10
    .whc_Height(40)    //view自身高度40
    .whc_Top(64)       //view與父視圖頂邊距64複製代碼

一行代碼計算Cell高度


func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return UITableViewCell.whc_CellHeightForIndexPath(indexPath, tableView: tableView)
}複製代碼

隱式更新約束


什麼叫隱式更新,顧名思義就是在你添加同類型約束(可能會產生衝突約束)會自動刪除前面添加可能產生衝突的約束(更新約束)看下面例子:git

override func viewDidLoad() {
    super.viewDidLoad()
    let view = UIView()
    self.view.addSubview(view)

    view.whc_Left(10)       //view與父視圖左邊距10
        .whc_Right(10)      //view與父視圖右邊距10
        .whc_HeightAuto()   //view高度自動
        .whc_Top(64)        //view與父視圖頂邊距64
}複製代碼

有時候程序執行的過程當中根據需求須要動態調整UI佈局假如點擊按鈕上面view高度調整固定64代碼以下:github

// 單獨更新height約束
private func clickButton(sender: UIButton) {
    view.whc_Height(64)   // 只須要執行這一行代碼便可更新view高度爲64
}複製代碼

上面這個代碼執行作了什麼事情呢?
他會檢查view高度方向是否有同類型可能衝突約束若是檢查到那麼會刪除上面添加的HeightAuto約束而後添加新Height固定約束64。
默認狀況固定高度約束優先級比自動高度約束高因此即便不刪除上面HeightAuto也不要緊,可是有時候會由於約束衝突程序崩潰。再好比點擊按鈕以下修改:swift

// 單獨更新top約束
private func clickButton(sender: UIButton) {
    view.whc_Top(10, toView: otherView)   //viwe 頂部間隙到otherView底部爲10
}複製代碼

一樣上面的代碼執行以前會檢查y方向是否有同類型約束(可能衝突的約束),顯然view.whc_Top(10)view.whc_Top(10, toView: otherView)確定是衝突的,因此在執行上面代碼WHC_AutoLayout會先刪除viewwhc_Top(10)約束而後再添加whc_Top(10, toView: otherView)約束。
上面解釋就是隱式更新約束技術而不須要像masonry從新重寫view全部約束那麼麻煩。api

UIView高度自動


override func viewDidLoad() {
    super.viewDidLoad()
    let view = UIView()
    self.view.addSubview(view)

    let label = UILabel()
    self.view.addSubview(label)
    label.text = "xxxxxxxxxxxxxxxxxxxxx"
    label.whc_Left(10)      //label左到view左邊距10
         .whc_Right(10)     //label右到view右邊距10
         .whc_Top(10)       //label頂到view頂邊距10
         .whc_HeightAuto()  //label高度自動
         .whc_Bottom(10, keepHeightConstraint: true) //label底到view底邊距10,而且保留label高度

    view.whc_Left(10)       //view與父視圖左邊距10
        .whc_Right(10)      //view與父視圖右邊距10
        .whc_Top(64)        //view與父視圖頂邊距64
        .whc_HeightAuto()   //view高度自動
}複製代碼

效果以下:
緩存


下面對上面代碼作解釋:
爲何label須要5個約束?那是由於 view高度須要自動根據 label高度自動調整,而 label高度自己是自動的若是不添加 labelview的底邊距 whc_Bottom關係約束 view沒法根據 label高度變化而變化。 那可能又有人疑問? whc_Bottom(10, keepHeightConstraint: true)裏的 keepHeightConstraint是什麼意思?前面介紹了 WHC_AutoLayout是隱式更新約束技術然而很顯然 labelwhc_HeightAuto通常狀況和 whc_Bottom是同類型約束(衝突約束)因此這兩個通常狀況只能存在一個約束,可是iOS有一種特殊狀況須要 Height約束和 Bottom約束同時存在那就是在 view自動高度的時候( bottom爲了撐開父視圖由於父視圖是自動高度因此須要一個自動高度參照約束)或者 view底邊距對齊(不採用 top對齊)的時候如:

label.whc_Left(10)      //label左到view左邊距10
     .whc_Right(10)     //label右到view右邊距10
     .whc_HeightAuto()  //label高度自動
     .whc_Bottom(10, keepHeightConstraint: true) //label底部間隙和父視圖底部10複製代碼

上面label就是一種從下往上佈局。iview

總結


從上面的例子與介紹能夠咱們能夠對WHC_AutoLayout得出以下結論:ide

  • 一個控件在不使用帶keep的約束API時候無論後面添加多少約束永遠只會存在4個
  • 一個控件同方向約束無論後面添加多少約束永遠只會保留最後添加的同類型約束自動刪除前面其餘同類型約束
  • 一個view須要高度或者寬度自動適應時候其view上最後一個控件須要用到5個約束(這個時候須要用到帶keep的API,一樣從下或者從右開始佈局有時候也須要)
  • 關於WHC_StackView後面會有專門的文章詳細介紹

    附件


1.x方向同類型約束(不會對寬度產生影響):
Left,Leading,Trailing,CenterX(包含ToView...)
注意WHC_AutoLayoutLeadingTrailing特殊處理理論上他們是能夠成對使用的爲了統一性把他們歸爲同類約束Leading左對齊Trailing又對齊
2.y方向同類型約束(不會對寬度產生影響):
Top,BaseLineSpace,CenterY(包含ToView...)
3.寬度方向同類型約束(對寬度產生影響):
Width,Right(包含ToView,自動寬度...)
4.高度方向同類型約束(對高度產生影響):
Height,Bottom(包含ToView,自動高度...)佈局


WHC_AutoLayout開源地址github.com/netyouli/WH…
本人其餘優秀開源項目:github.com/netyouli/

致敬

謝謝你的耐心閱讀

掘金徵文: gold.xitu.io/post/58522d…

相關文章
相關標籤/搜索