TableView计算错误的estimateHeightForRowAt

时间:2019-01-17 23:00:22

标签: swift tableview scrollview

我正在做一个类似于聊天的应用程序,其中tableView显示动态高度单元格。


单元格的视图和子视图以正确的方式受到约束

  

以便 AutoLayout 可以预测单元格的高度

     

(顶部,底部,前导,尾随)


但是仍然-如您在视频中看到的那样-滚动指示器栏显示计算出的高度不正确:

它会在出现新行时重新计算高度。

视频: https://youtu.be/5ydA5yV2O-Q

(第二次尝试向下滚动都可以)


代码:

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}

这是一个简单的问题。有人可以帮我吗?

更新1.0

添加了github:

https://github.com/krptia/Test

10 个答案:

答案 0 :(得分:3)

您需要将tableFooterView设置为空。

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.tableFooterView = UIView()
    // your staff
}

答案 1 :(得分:2)

问题出在您的estimatedHeightForRowAt方法上。顾名思义,它为表格提供了估计的高度,因此它可以对可滚动内容有所了解,直到显示实际内容为止。值越精确,滚动和高度估算就越平滑。

您应该将此值设置为足够大,以便可以用最大的内容表示单元格的高度。您的情况下650可以正常工作。

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return 650
}

使用这种方法的结果会更好。

此外,在您需要基于索引的变体之前,无需实现委托高度的方法。您只需设置表格视图属性即可。

tableView.estimatedRowHeight = 650.0
tableView.rowHeight = .automaticDimension

优化

我在您的演示项目中注意到了另一件事。您在if-else中使用了过多的cellForRowAtIndexPath,这使它变慢了一点。尽量减少这种情况。我对此做了一些改进,它提高了性能。

  1. 定义一个用于保存消息文本的数组。

    var messages = ["Lorem ipsum,"many more",.....]

  2. 将您的cellForRowAt indexPath替换为以下内容:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { var cell : WorldMessageCell cell = tableView.dequeueReusableCell(withIdentifier: "WorldMessageCell", for: indexPath) as! WorldMessageCell if indexPath.row < 14 { cell.messageLabel.text = messages[indexPath.row] } else if indexPath.row >= 14 && indexPath.row != 27 { cell.messageLabel.text = messages[14] } else if indexPath.row == 27 { cell.messageLabel.text = messages.last } return cell }

答案 2 :(得分:2)

根据您对我的评论的回答,当您设置

  

estimatedHeightForRowAt和heightForRowAt具有相同的值   工作

我可以确认您是对的,并且存在AutoLayout无法计算estimatedHeightForRowAt的正确值的问题。因此,基本上有两种可能要做的事情:

  • 找到可以产生更好结果的替代布局
  • 为自己估计的HeightHeightForRowAt进行计算,这将产生更准确的结果(通常,您应该能够知道每个文本长度的预期高度,然后在该数字上添加页边距-您需要付出一些努力才能找到正确的数学,但应该可以)。

答案 3 :(得分:2)

  

但仍然-如您在视频中看到的-滚动指示器栏显示计算出的错误高度:

所以您想要的是精确的内容高度。

为此,您不能使用静态estimatedRowHeight。 您应该像下面那样实现更正确的估算。

    ...

    var sampleCell: WorldMessageCell?

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(UINib(nibName: "WorldMessageCell", bundle: nil), forCellReuseIdentifier: "WorldMessageCell")

        sampleCell = UINib(nibName: "WorldMessageCell", bundle: nil).instantiate(withOwner: WorldMessageCell.self, options: nil)[0] as? WorldMessageCell
    }

    ...

    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        if let cell = sampleCell {
            let text = self.textForRowAt(indexPath)
            // note: this is because of "constrain to margins", which value is actually set after estimation. Do not use them to remove below
            let margin = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20)
            // without "constrain to margins"
            // let margin = cell.contentView.layoutMargins 
            let maxSize = CGSize(width: tableView.frame.size.width - margin.left - margin.right,
                                 height: CGFloat.greatestFiniteMagnitude)
            let attributes: [NSAttributedString.Key: Any]? = [NSAttributedString.Key.font: cell.messageLabel.font]
            let size: CGRect = (text as NSString).boundingRect(with: maxSize,
                                                                 options: [.usesLineFragmentOrigin], attributes: attributes, context: nil)
            return size.height + margin.top + margin.bottom
        }
        return 100
    }

这太精确了(实际上是真实的行高),也许还很慢,但是您可以为优化进行更多近似估算。

答案 4 :(得分:2)

enter image description here

只需从UITableView中删除突出显示的视图,就可以像超级按钮一样工作。

enter image description here

希望有帮助。

答案 5 :(得分:2)

使用粗略的单元格高度估计(或完全不提供它们,就像您所做的那样)时,这是预期的行为。仅当单元格出现在屏幕上时才计算实际高度,因此此时会调整滚动条的行程。如果使用它们,也希望有快速的插入/删除动画。

答案 6 :(得分:0)

为什么要在表视图中添加视图,没有它也可以工作。我只是删除了该视图并更改了一些约束(例如底部约束将安全区域更改为超级视图),所以效果很好。

see this video

download storyboard and add it to your project and then check

答案 7 :(得分:0)

viewDidLoad()

中使用这些配置表视图
        tableView.estimatedRowHeight = 100.0
        tableView.rowHeight = UITableView.automaticDimension
        tableView.tableFooterView = UIView()

您应该删除两个高度数据源方法。

答案 8 :(得分:0)

您想做的是eliminate the extra blank cells。您可以通过在tableFooterView方法中将UIView设置为空的viewDidLoad来实现。我从您的GitHub克隆了代码并修改了方法:

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.register(UINib(nibName: "WorldMessageCell", bundle: nil), forCellReuseIdentifier: "WorldMessageCell")
    tableView.tableFooterView = UIView()
}

tableFooterView设置为nil也对我有用

tableView.tableFooterView = nil

enter image description here

答案 9 :(得分:0)

希望您能听到很多。因此请稍事休息,然后再回到办公桌上,并执行2-3步。

1)确保“单元格标签的自动版式”设置正确,如下所示。

enter image description here

2)UILabel的行数为文本的动态高度设置为零。

enter image description here

3)设置单元格的自动尺寸高度。

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}

,我相信它应该起作用。查看我的代码结果。

enter image description here