带有表格视图和自定义表格视图单元格的iOS自动布局的谜团

时间:2020-10-21 16:09:08

标签: ios uitableview

为了帮助解决这个问题,我建立了一个GitHub存储库:

https://github.com/mattneub/SelfSizingCells/tree/master

目标是在表格视图中获得自定义大小的单元格,该表格基于绘制自己的文本而不是UILabel的自定义视图。我可以做到,但是它涉及到一个奇怪的布局错误,我不明白为什么需要它。时间安排似乎有些问题,但后来我不明白为什么UILabel不会出现相同的问题。

为了演示,我将示例分为三个场景。

场景1:UILabel

在第一个场景中,每个单元格都包含一个UILabel,固定在内容视图的所有四个侧面上。我们要求自我调整大小的单元格,然后得到它们。看起来很棒。

场景2:StringDrawer

在第二个场景中,UILabel已被名为StringDrawer的自定义视图替换,该视图绘制了自己的文本。就像标签一样,它固定在内容视图的所有四个侧面。我们要求自定义大小的单元格,但是如何获得它们?

为解决此问题,我根据显示的字符串给StringDrawer一个intrinsicContentSize。基本上,我们测量字符串并返回结果大小。特别是,高度将是此视图所需的最小高度,以便以该视图的当前宽度完整显示字符串,并且单元格的大小应为此设置。

class StringDrawer: UIView {
    @NSCopying var attributedText = NSAttributedString() {
        didSet {
            self.setNeedsDisplay()
            self.invalidateIntrinsicContentSize()
        }
    }
    
    override func draw(_ rect: CGRect) {
        self.attributedText.draw(with: rect, options: [.truncatesLastVisibleLine, .usesLineFragmentOrigin], context: nil)
    }
    
    override var intrinsicContentSize: CGSize {
        let measuredSize = self.attributedText.boundingRect(
            with: CGSize(width:self.bounds.width, height:10000),
            options: [.truncatesLastVisibleLine, .usesLineFragmentOrigin],
            context: nil).size
        return CGSize(width: UIView.noIntrinsicMetric, height: measuredSize.height.rounded(.up) + 5)
    }
}

但是出了点问题。在此场景中,一些初始单元格的底部有一些额外的空白。此外,如果将这些单元格从视图中滚动出来然后又重新回到视图中,则它们看起来是正确的。其他所有单元格看起来都很好。那证明我在做的事是正确的,那么为什么它不能用于初始单元格?

好吧,我已经做了一些繁重的日志记录,并且发现在最初为可见单元格调用intrinsicContentSize时,StringDrawer尚未正确知道其自身的最终宽度,即自动布局后将具有。我们被称为太早了。我们使用的宽度太窄,因此返回的高度太高。

场景3:具有解决方法的StringDrawer

在第三个场景中,我为在第二个场景中发现的问题添加了一种解决方法。效果很好!但这太可怕了。基本上,在视图控制器中,我等到视图层次结构组装好后,再通过调用beginUpdatesendUpdates强制表视图进行另一轮布局。

var didInitialLayout = false
override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    if !didInitialLayout {
        didInitialLayout = true
        UIView.performWithoutAnimation {
            self.tableView.beginUpdates()
            self.tableView.endUpdates()
        }
    }
}

神秘之物

好的,这是我的问题:

(1)是否有更好,更省心的解决方法?

(2)为什么我们完全需要这种解决方法?特别是为什么我们的StringDrawer遇到这个问题,而UILabel却不是?显然,UIlabel 确实足够早地知道其自身的宽度,以便在布局系统对其进行查询时,可以在第一遍正确给出自己的内容大小。为什么我的StringDrawer与此不同?为什么需要额外的布局通行证?

0 个答案:

没有答案