简体   繁体   English

UITableView + 自动布局 + 固有尺寸:在何处/何时计算尺寸?

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

I have create a custom view MyIntrincView which calculates its height automatically when setting its content.我创建了一个自定义视图MyIntrincView ,它在设置其内容时自动计算其高度。 This works fine both in simulator and InterfaceBuilder.这在模拟器和 InterfaceBuilder 中都可以正常工作。

However, when placing MyIntrinsicView inside a UITableViewCell , the cell height is not calculated correctly.但是,将MyIntrinsicView放在UITableViewCell中时,单元格高度计算正确。 Instead of automatically adopting the cell height to the intrinsic height of the view, all cell keep the same, initial height.所有单元格都保持相同的初始高度,而不是自动将单元格高度应用于视图的固有高度。

// 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
}

I would expect, that each cell has a different height ( 20 * someProperty = 20 * indexPath.row ).我希望每个单元格都有不同的高度( 20 * someProperty = 20 * indexPath.row )。 However, instead all cell have the same, initial height.但是,所有单元格都具有相同的初始高度。

Only when explicitly calling myIntrinsicView.layoutSubviews() the cells are created with the correct height.只有当显式调用myIntrinsicView.layoutSubviews()时,才会以正确的高度创建单元格。

It seems that the tableView does not call myIntrinsicView.layoutSubviews() .似乎 tableView 没有调用myIntrinsicView.layoutSubviews() Why is this?为什么是这样?

When using a UILabel instad of MyIntrinsicView as cell content with different text lengths, everything works as expected.当使用UILabel instad 的MyIntrinsicView作为具有不同文本长度的单元格内容时,一切都按预期工作。 Thus the overall tableView setup is correct (= cell sizes are calculated automatically) and there has to be way to use intrinsic sizes correctly in UITableView as well.因此,整个 tableView 设置是正确的(= 单元格大小是自动计算的),并且必须有办法在UITableView中正确使用固有大小。 So, what exactly is the correct way to do this?那么,这样做的正确方法到底是什么?

As with your previous question here I think you're not really understanding what intrinsicContentSize does and does not do.与您之前的问题一样我认为您并没有真正理解intrinsicContentSize做什么和不做什么。

When setting the text of a UILabel , yes, its intrinsicContentSize changes, but that's not all that happens.设置UILabel的文本时,是的,它的intrinsicContentSize会发生变化,但这还不是全部。

You also don't want to do what you're trying inside layoutSubviews() ... if your code does trigger a change, you'll get into an infinite recursion loop (again, as with your previous question).您也不想在layoutSubviews()中执行您正在尝试的操作......如果您的代码确实触发了更改,您将进入无限递归循环(再次,与您之前的问题一样)。

Take a look at modifications to your code:看看对您的代码的修改:

// 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
        }
    }
}

Two side notes...两个旁注...

1 - Your posted code shows you calling myIntrinsicView.layoutSubviews() ... from Apple's docs: 1 - 您发布的代码显示您从 Apple 的文档中调用myIntrinsicView.layoutSubviews() ...:

You should not call this method directly.您不应直接调用此方法。 If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update.如果要强制更新布局,请在下一次绘图更新之前调用 setNeedsLayout 方法。 If you want to update the layout of your views immediately, call the layoutIfNeeded method.如果您想立即更新视图的布局,请调用 layoutIfNeeded 方法。

2 - For the direction it looks like you're headed, you would probably be better off manipulating constraint constants, rather than intrinsic content size. 2 - 对于看起来像你前进的方向,你可能会更好地操纵约束常数,而不是内在的内容大小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM