动态更改UITableViewCell的高度-Swift 4

时间:2018-09-10 22:22:43

标签: ios swift uitableview heightforrowatindexpath

如何动态更改UITableViewCell的高度?我尝试实现以下内容,但由于某种原因,它无法正常工作。一旦加载显示此表视图的视图控制器,代码便会崩溃

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let cell = tableView.cellForRow(at: indexPath) as! AvailableRideCell
    return cell.getHeight()
}

这是我的getHeight中的AvailableRideCell函数

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

3 个答案:

答案 0 :(得分:1)

请记住,UITableViewCell已被重用。因此获取当前单元格的高度可能会很不稳定。

一种更好的方法是使用一个伪造/占位符单元格(我称为计算器单元格),并使用该单元格来计算该单元格的大小。

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

答案 1 :(得分:0)

您的代码由于此行而崩溃

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

根据Apple Documentation,我们知道此方法用于优化。整个想法是在不浪费时间创建单元本身的情况下获得单元高度。因此,这种称为 before 的方法将初始化任何单元格,并且tableView.cellForRow(at: indexPath)返回nil。因为还没有任何单元格。但是您用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)

希望它对您有帮助。

答案 2 :(得分:0)

您没有提到是否使用自动版式,但如果使用自动版式,则可以让自动版式管理每行的高度。您无需实现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)
    ])

每行将扩展或收缩以适合标签固有内容的大小。如果设备横向/纵向改变,则高度会自动调整,而无需重新加载单元。如果您希望当设备的UIContentSizeCategory更改时行高自动更改,请设置以下内容:

cellLabel.adjustsFontForContentSizeCategory = true