【積少成多】Swift - layoutSubviews、layoutIfNeeded、setNeedsLayout

對於 iOS 中的視圖佈局,正確的使用上面的三個方法是很是重要的,下面就來深刻的講解一下三個方法的做用以及不一樣之處。ios

在開始以前先解釋一個詞:更新週期(update cycle)。swift

所謂的更新週期,即更新當前屏幕視圖的一個過程,它會在當前 run loop 結束時開始。bash

setNeedsLayout()

調用該方法:即告訴 App 在更新週期時,佈局和重繪當前視圖及其子視圖。異步

該方法是異步操做且必須在主線程中調用,它調用完成會當即返回。由於你不知道更新週期何時會開始,因此,調用該方法不能精準的控制視圖約束更新的時間。可是因爲該方法能夠將多個佈局更新在一個更新週期中更新,所以它的性能會更好。oop

layoutIfNeeded()

調用該方法:即告訴 App 當即佈局和重繪當前視圖及其子視圖,不須要等待更新週期。佈局

該方法是同步操做,佈局更新和視圖重繪已在該方法調用完成以前所有更新。post

layoutSubviews()

該方法會根據你設置的約束來肯定子視圖的位置和尺寸。性能

當子視圖執行更加精細的佈局時,能夠重寫該方法。只有當子視圖的自動調整(autoresizing)和基於約束的行爲不能提供所需的行爲時,才應該重寫此方法。動畫

不要直接調用該方法,若是你想強制更新佈局,能夠調用 setNeedsLayout() 方法,若是想馬上更新佈局,能夠調用 layoutIfNeeded() 方法。ui

應用

// 1. 在delegate 方法中調用 layoutIfNeeded() 方法
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // 1.1 cell 的具體配置
    xxxxx
    // 1.2 調用 layoutIfNeeded()
    cell.layoutIfNeeded()
    return cell
}

// 2. 改變相應約束
xxConstraints.const = 100

// 3. 全文/收起 按鈕的點擊事件 reload 相應 cell
@objc private buttonAction() {
    tableView.reloadRows(at: [xxIndexPath], with: .automatic)   
}
複製代碼

約束改變的最佳實踐

Apple Document: It is almost always cleaner and easier to update a constraint immediately after the affecting change has occurred. Deferring these changes to a later method makes the code more complex and harder to understand.

Apple 文檔推薦咱們在發生約束改變的狀況時,當即修改約束是最簡單明瞭的方式。好比在按鈕點擊事件中須要更新約束時的代碼:

@IBAction func buttonAction(_ sender: UIButton) {
    animationChange()
}
 
private func animationChange() {
    // 在須要改變時,首先改變約束
    heightConstraint.constant = 300
    //在根據需求處理響應邏輯
    UIView.animate(withDuration: 2.0) {
        self.view.layoutIfNeeded()
    }
}
複製代碼

總結

  • layoutSubviews():不要直接調用,能夠在子類中重寫該方法以得到子視圖更加精細的佈局。
  • layoutIfNeeded():同步、當即更新重繪當前視圖及子視圖,不等待更新週期。
  • setNeedsLayout():異步、 在更新週期中更新佈局,性能更佳。

參考

相關文章
相關標籤/搜索