简体   繁体   English

动态更改UITableViewCell的高度-Swift 4

[英]Dynamically change UITableViewCell height - Swift 4

How do I go about dynamically changing the UITableViewCell height? 如何动态更改UITableViewCell高度? I've tried implementing the following, but for some reason, it isn't working. 我尝试实现以下内容,但由于某种原因,它无法正常工作。 The code crashes as soon as I load the view controller displaying this table view 一旦加载显示此表视图的视图控制器,代码便崩溃

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

This is the getHeight function in my AvailableRideCell 这是我的AvailableRideCellgetHeight函数

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

And this is the optimalHeight function 这是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
        }
    }
}

Keep in mind that UITableViewCell is reused. 请记住,UITableViewCell已被重用。 So getting the height of the current cell can be unstable. 因此,获取当前单元格的高度可能会很不稳定。

A better way is to have one fake/placeholder cell (I call the calculator cell) and use that to calculate the size of the cell. 更好的方法是使用一个假/占位符单元格(我称其为计算器单元格),然后使用该单元格来计算该单元格的大小。

So in the heightForRowAt method, you get the data instead of the cell. 因此,在heightForRowAt方法中,您将获得数据而不是单元格。 Put that data inside the calculator cell and get the height from there. 将该数据放入计算器单元格,然后从那里获取高度。

You code crashes because of this line 您的代码因此行而崩溃

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

From Apple Documentation we know that this method is used for optimization. Apple文档中我们知道该方法用于优化。 The whole idea is to get cells heights without wasting time to create the cells itself. 整个想法是在不浪费时间创建单元本身的情况下获得单元高度。 So this method called before initializing any cell, and tableView.cellForRow(at: indexPath) returns nil . 因此, 初始化任何单元之前调用此方法,并且tableView.cellForRow(at: indexPath)返回nil Because there are no any cells yet. 因为还没有任何单元格。 But you're making force unwrapping with as! AvailableRideCell 但是,您正在用as! AvailableRideCell解开力量as! AvailableRideCell as! AvailableRideCell and your code crashed. as! AvailableRideCell和您的代码崩溃了。

So at first, you need to understand , why you should not use any cells inside the cellForRow(at ) method. 因此,首先,您需要了解 ,为什么不应该使用cellForRow(at )方法内的任何单元格。 After that, you need to change the logic so you could compute content height without calling a cell. 之后,您需要更改逻辑,以便无需调用单元即可计算内容高度。

For example, in my projects, I've used this solution 例如,在我的项目中,我使用了此解决方案

String implementation 字符串实现

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

With that, all you need to do is to compute your label and store its font. 这样,您所需要做的就是计算标签并存储其字体。

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

Dynamic rows height changing 动态行高变化

If you'll need to update row height, all you need to do is to call this method when your content had been changed. 如果您需要更新行高,那么您要做的就是在更改内容后调用此方法。 indexPath is the index path of changed item. indexPath是已更改项目的索引路径。

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

Hope it helps you. 希望对您有帮助。

You don't mention if you are using Auto Layout, but if you are, you can let Auto Layout manage the height of each row. 您没有提到是否使用自动版式,但是如果您使用自动版式,则可以让自动版式管理每行的高度。 You don't need to implement heightForRow , instead set: 您不需要实现heightForRow ,而是设置:

tableView.rowHeight = UITableViewAutomaticDimension

And configure your UILabel with constraints that pin it to the cell's content view: 并使用将其固定到单元格内容视图的约束条件配置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)
    ])

Each row will expand or contract to fit the label's intrinsic content size. 每行都会扩展或收缩以适合标签固有内容的大小。 The height is automatically adjusted if the device landscape/portrait orientation changes, without re-loading the cell. 如果设备横向/纵向改变,则高度会自动调整,而无需重新加载单元。 If you want the row height to change automatically when the device's UIContentSizeCategory changes, set the following: 如果您希望当设备的UIContentSizeCategory更改时行高自动更改,请设置以下内容:

cellLabel.adjustsFontForContentSizeCategory = true

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

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