簡體   English   中英

UITableView + 自動布局 + 固有尺寸:在何處/何時計算尺寸?

[英]UITableView + AutoLayout + Intrinsic Size: Where/when to calculate size?

我創建了一個自定義視圖MyIntrincView ,它在設置其內容時自動計算其高度。 這在模擬器和 InterfaceBuilder 中都可以正常工作。

但是,將MyIntrinsicView放在UITableViewCell中時,單元格高度計算正確。 所有單元格都保持相同的初始高度,而不是自動將單元格高度應用於視圖的固有高度。

// A simple, custom view with intrinsic height. The height depends on
// the value of someProperty. When the property is changed setNeedsLayout
// is set and the height changes automatically.
@IBDesignable class MyIntrinsicView: UIView {
    @IBInspectable public var someProperty: Int = 5 {
        didSet { setNeedsLayout() }
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        calcContent()
    }
    
    func calcContent() {
        height = CGFloat(20 * someProperty)
        invalidateIntrinsicContentSize()
    }
    
    
    @IBInspectable var height: CGFloat = 50
    override var intrinsicContentSize: CGSize {
        return CGSize(width: super.intrinsicContentSize.width, height: height)
    }
    
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        invalidateIntrinsicContentSize()
    }
}


// A simple cell which only contains a MyIntrinsicView subview. The view
// is attached to trailing, leading, top and bottom anchors of the cell.
// Thus the cell height should automatically match the height of the
// MyIntrinsicView
class MyIntrinsicCell: UITableViewCell {
    @IBOutlet private var myIntrinsicView: MyIntrinsicView!
    
    var someProperty: Int {
        get { return myIntrinsicView.someProperty }
        set {
            myIntrinsicView.someProperty = newValue
            
            // Cell DOES NOT rezise without manualle calling layoutSubviews()
            myIntrinsicView.layoutSubviews()
        }
    }
}

...
// Simple tableView delegate which should create cells of different heights 
// by giving each cell a different someProperty 
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "IntrinsicCell", for: indexPath) as? MyIntrinsicCell ?? MyIntrinsicCell()
    
    // Give all cell a different intrinsic height by setting someProperty to rowIndex
    cell.someProperty = indexPath.row
    return cell
}

我希望每個單元格都有不同的高度( 20 * someProperty = 20 * indexPath.row )。 但是,所有單元格都具有相同的初始高度。

只有當顯式調用myIntrinsicView.layoutSubviews()時,才會以正確的高度創建單元格。

似乎 tableView 沒有調用myIntrinsicView.layoutSubviews() 為什么是這樣?

當使用UILabel instad 的MyIntrinsicView作為具有不同文本長度的單元格內容時,一切都按預期工作。 因此,整個 tableView 設置是正確的(= 單元格大小是自動計算的),並且必須有辦法在UITableView中正確使用固有大小。 那么,這樣做的正確方法到底是什么?

與您之前的問題一樣我認為您並沒有真正理解intrinsicContentSize做什么和不做什么。

設置UILabel的文本時,是的,它的intrinsicContentSize會發生變化,但這還不是全部。

您也不想在layoutSubviews()中執行您正在嘗試的操作......如果您的代碼確實觸發了更改,您將進入無限遞歸循環(再次,與您之前的問題一樣)。

看看對您的代碼的修改:

// A simple, custom view with intrinsic height. The height depends on
// the value of someProperty. When the property is changed setNeedsLayout
// is set and the height changes automatically.
@IBDesignable class MyIntrinsicView: UIView {
    @IBInspectable public var someProperty: Int = 5 {
        didSet {
            calcContent()
            setNeedsLayout()
        }
    }
    
    func calcContent() {
        height = CGFloat(20 * someProperty)
        invalidateIntrinsicContentSize()
    }
    
    
    @IBInspectable var height: CGFloat = 50
    override var intrinsicContentSize: CGSize {
        return CGSize(width: super.intrinsicContentSize.width, height: height)
    }
    
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        invalidateIntrinsicContentSize()
    }
}


// A simple cell which only contains a MyIntrinsicView subview. The view
// is attached to trailing, leading, top and bottom anchors of the cell.
// Thus the cell height should automatically match the height of the
// MyIntrinsicView
class MyIntrinsicCell: UITableViewCell {
    @IBOutlet private var myIntrinsicView: MyIntrinsicView!
    
    var someProperty: Int {
        get { return myIntrinsicView.someProperty }
        set {
            myIntrinsicView.someProperty = newValue
        }
    }
}

兩個旁注...

1 - 您發布的代碼顯示您從 Apple 的文檔中調用myIntrinsicView.layoutSubviews() ...:

您不應直接調用此方法。 如果要強制更新布局,請在下一次繪圖更新之前調用 setNeedsLayout 方法。 如果您想立即更新視圖的布局,請調用 layoutIfNeeded 方法。

2 - 對於看起來像你前進的方向,你可能會更好地操縱約束常數,而不是內在的內容大小。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM