动态更改 UITableViewCell 高度 - Swift 4

Dynamically change UITableViewCell height - Swift 4

如何动态更改 UITableViewCell 高度?我试过执行以下操作,但由于某种原因,它不起作用。一旦我加载显示此 table 视图

的视图控制器,代码就会崩溃
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let cell = tableView.cellForRow(at: indexPath) as! AvailableRideCell
    return cell.getHeight()
}

这是我的 AvailableRideCell

中的 getHeight 函数
func getHeight() -> CGFloat {
    return self.destinationLabel.optimalHeight + 8 + self.originLabel.optimalHeight + 8 + self.priceLabel.optimalHeight
}

这是optimalHeight函数

extension UILabel {
    var optimalHeight : CGFloat {
        get {
            let label = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude))
            label.numberOfLines = 0
            label.lineBreakMode = NSLineBreakMode.byWordWrapping
            label.font = self.font
            label.text = self.text
            label.sizeToFit()
            return label.frame.height
        }
    }
}

请记住,UITableViewCell 是重复使用的。所以获取当前cell的高度会不稳定

更好的方法是使用一个 fake/placeholder 单元格(我称之为计算器单元格)并使用它来计算单元格的大小。

因此在 heightForRowAt 方法中,您获取的是数据而不是单元格。 将该数据放入计算器单元格中,然后从那里获取高度。

你的代码因为这一行而崩溃

let cell = tableView.cellForRow(at: indexPath) as! AvailableRideCell

Apple Documentation我们知道这个方法是用来做优化的。整个想法是在不浪费时间创建单元格本身的情况下获得单元格高度。所以这个方法调用before初始化任何单元格,和tableView.cellForRow(at: indexPath)returnsnil。因为还没有任何细胞。但是你用 as! AvailableRideCell 强制解包,你的代码崩溃了。

所以首先,您需要了解,为什么您不应该在cellForRow(at )方法中使用任何单元格。 之后,您需要更改逻辑,以便在不调用单元格的情况下计算内容高度。

例如,在我的项目中,我使用了这个解决方案

字符串实现

extension String {
    func height(for width: CGFloat, font: UIFont) -> CGFloat {
        let maxSize = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
        let actualSize = self.boundingRect(with: maxSize,
                                           options: [.usesLineFragmentOrigin],
                                           attributes: [NSAttributedStringKey.font: font],
                                           context: nil)
        return actualSize.height
    }
}

UILabel 实现

extension String {
    func height(for width: CGFloat, font: UIFont) -> CGFloat {
        let labelFrame = CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude)
        let label = UILabel(frame: labelFrame)
        label.numberOfLines = 0
        label.lineBreakMode = .byWordWrapping
        label.font = font
        label.text = self
        label.sizeToFit()
        return label.frame.height
    }
}

这样,您需要做的就是计算标签并存储其字体。

var titles = ["dog", "cat", "cow"]

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // number of rows is equal to the count of elements in array
    return titles.count
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let cellTitle = titles[indexPath.row]
    return cellTitle.height(forWidth: labelWidth, font: labelFont)
}

动态行高变化

如果您需要更新行高,您只需在内容更改后调用此方法即可。 indexPath 是更改项的索引路径。

tableView.reloadRows(at: [indexPath], with: .automatic)

希望对你有帮助。

你没有提到你是否在使用自动布局,但如果你是,你可以让自动布局管理每一行的高度。您不需要实施 heightForRow,而是设置:

tableView.rowHeight = UITableViewAutomaticDimension

并使用将其固定到单元格内容视图的约束配置您的 UILabel:

    let margins = contentView.layoutMarginsGuide
    NSLayoutConstraint.activate([
        cellLabel.leadingAnchor.constraint(equalTo: margins.leadingAnchor),
        cellLabel.trailingAnchor.constraint(equalTo: margins.trailingAnchor),
        cellLabel.topAnchor.constraint(equalTo: margins.topAnchor),
        cellLabel.bottomAnchor.constraint(equalTo: margins.bottomAnchor)
    ])

每一行都会扩展或收缩以适应标签的固有内容大小。如果设备 landscape/portrait 方向发生变化,高度会自动调整,而无需重新加载单元格。如果你希望当设备的 UIContentSizeCategory 改变时行高自动改变,设置如下:

cellLabel.adjustsFontForContentSizeCategory = true