如何在自定义表格单元格中实现具有动态内容的动态项目?

时间:2019-07-15 11:20:47

标签: ios swift autolayout

如果内容在自定义表格视图单元格中所占的比超级视图多,我该如何动态地逐个动态地调整内容大小?

请参考图片以澄清问题。

enter image description here

1 个答案:

答案 0 :(得分:0)

您发布的问题有点长,实际上要进行多项操作,因为您没有发布尝试过的内容以及遇到问题的部分。有很多方法可以做到这一点,所以让我们从普通标签开始:

UILabel可以在attributedText下具有属性字符串,该字符串可用于实现图像中显示的视觉设计。要水平放置它们,我们可以使用sizeToFit来获取它们的宽度。假设像这样:

func layoutLabels(_ labels: [UILabel], inView view: UIView, horizontalSeparator: CGFloat = 4.0) {
    var x: CGFloat = 0.0
    labels.forEach { label in
        label.sizeToFit() // Will make it's size just right
        label.frame = CGRect(x: x, y: 0.0, width: label.frame.width, height: label.frame.height)
        x = label.frame.maxX + horizontalSeparator
        view.addSubview(label)
    }
}

现在要添加垂直部分,我们只需要添加最大宽度并添加y分量:

func layoutLabels(_ labels: [UILabel], inView view: UIView, maximumWidth: CGFloat, lineHeight: CGFloat, horizontalSeparator: CGFloat = 4.0, verticalSeparator: CGFloat = 2.0) {
    var x: CGFloat = 0.0
    var y: CGFloat = 0.0
    labels.forEach { label in
        label.sizeToFit() // Will make it's size just right
        var newX = x + label.frame.width

        if newX > maximumWidth {
            if x == 0.0 {
                // We are at the beginning of the line and a single label is simply too large. We will need to reduce it's width
                label.frame = CGRect(x: x, y: y, width: maximumWidth, height: label.frame.height)
            } else {
                // We should go into new line
                x = 0.0
                y += lineHeight
                label.frame = CGRect(x: x, y: y, width: min(label.frame.width, maximumWidth), height: label.frame.height)
                newX = x + label.frame.width
            }
        } else {
            label.frame = CGRect(x: x, y: y, width: min(label.frame.width, maximumWidth), height: label.frame.height)
        }

        x = newX + horizontalSeparator
        view.addSubview(label)
    }
}

现在,您应该可以像在图像上那样填充标签。我们还将检查换行符和标签是否不适合您的视图的整个宽度。因此,现在我们需要定义视图的实际大小。 y应该返回该值,但我们需要能够在表视图单元格中使用它。所以我们可以简单地返回它:

func layoutLabels(_ labels: [UILabel], inView view: UIView, maximumWidth: CGFloat, lineHeight: CGFloat, horizontalSeparator: CGFloat = 4.0, verticalSeparator: CGFloat = 2.0) -> CGFloat {
    ...
    return labels.count > 0 ? y + lineHeight : 0.0
}

现在在表格视图单元格中,最好使用一些约束出口:

protocol Tag {
    func createLabel() -> UILabel
}

class Cell: UITableViewCell {

    @IBOutlet private var tagsPanel: UIView!
    @IBOutlet private var tagsPanelHeightConstraint: NSLayoutConstraint?

    var tags: [Tag]!

    func refresh() {
        tagsPanel.subviews.forEach { $0.removeFromSuperview() } // Ugly, needs improvement
        let height = layoutLabels(tags.map { $0.createLabel() }, inView: tagsPanel, maximumWidth: tagsPanel.frame.width, lineHeight: 40.0)
        tagsPanelHeightConstraint?.constant = height
    }

}

现在,问题在于您的表格视图需要刷新,因为表格视图单元格可能已调整大小。您将需要参考您的表视图,并对其进行简单的调用开始/结束更新。

var tags: [Tag]!

weak var tableView: UITableView? // Assigned in cellForRowAtIndexPath

func refresh() {
    tagsPanel.subviews.forEach { $0.removeFromSuperview() } // Ugly, needs improvement
    let height = layoutLabels(tags.map { $0.createLabel() }, inView: tagsPanel, maximumWidth: tagsPanel.frame.width, lineHeight: 40.0)
    if let tagsPanelHeightConstraint = tagsPanelHeightConstraint, tagsPanelHeightConstraint.constant != height {
        tagsPanelHeightConstraint.constant = height
        tableView?.beginUpdates()
        tableView?.endUpdates()
    }

}

祝你好运。