在 iOS Swift 中动态调整 UITableViewCell 高度

Posted

技术标签:

【中文标题】在 iOS Swift 中动态调整 UITableViewCell 高度【英文标题】:Dynamically Resizing UITableViewCell Heights in iOS Swift 【发布时间】:2016-04-06 18:29:28 【问题描述】:

我正在尝试创建一个 UITableView,其单元格的高度根据显示的文本量来调整大小。这看起来很简单,因为heightForRowAtIndexPath 可用,我可以使用下面显示的代码获得UILabel 所需的大小。但是,这种方法被称为 before cellForRowAtIndexPath 所以我不能根据单元格中的内容调整大小,这对我来说似乎是一个非常奇怪的选择。根据内容动态调整单元格高度的“最佳实践”方法是什么?

另外,为什么Apple没有在cellForRowAtIndexPath之后调用heightForRowAtIndexPath方法?

我在cellForRowAtIndexPath 中使用以下内容来确定我的标签所需的高度,但我不确定如何将其移至heightForRowAtIndexPath,因为无法在该方法中调用dequeReusableCellWithIdentifier(就我而言知道):

func getRequiredLabelHeight(label: UILabel, text: String, font: UIFont) -> CGFloat 
    label.numberOfLines = 0
    label.lineBreakMode = .ByWordWrapping
    label.font = font
    label.text = text
    label.sizeToFit()

    return label.frame.height


我也知道我希望以下是我的单元格高度,因为我希望单元格大小根据新标签大小(可以是任意长度)从初始大小(铰接在 1 行标签上)增加行数)

self.cellHeight = getRequiredLabelHeight(commentCell.commentTextLabel, text: allComments![indexPath.row].text, font: UIFont(name: "Avenir", size: 16.0)!) + commentCell.bounds.height - commentCell.commentTextLabel.bounds.height

谢谢!

编辑 我的问题与调整标签大小无关。我提供的代码就是这样做的。问题是单元高度不是恒定的。它根据内容在整个表格中有所不同,因此我不能使用静态tableview.rowHeight。由于我无法从cellForRowAtIndexPath 调整单元格高度,因此我的内容将不适合单元格。例如,这是我的cellForRowAtIndexPath(简而言之):

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 

    let commentCell: CommentTableViewCell = tableView.dequeueReusableCellWithIdentifier("commentTableViewCell", forIndexPath: indexPath)
        as! CommentTableViewCell

    commentCell.commentTextLabel.textColor = Utils.hatchliTextDark
    commentCell.commentTextLabel.font = UIFont(name: "Avenir", size: 16)

    self.cellHeight = getRequiredLabelHeight(commentCell.commentTextLabel, text: allComments![indexPath.row].text, font: UIFont(name: "Avenir", size: 16.0)!) + commentCell.bounds.height - commentCell.commentTextLabel.bounds.height

    commentCell.commentTextLabel.frame = CGRectMake(commentCell.commentTextLabel.frame.origin.x, commentCell.commentTextLabel.frame.origin.y, commentCell.commentTextLabel.frame.width, self.cellHeight)
    commentCell.handleTextLabel.text = String(allComments![indexPath.row].creatorHandle)
    commentCell.votesTextLabel.text = String(allComments![indexPath.row].votes)
    commentCell.commentTextLabel.text = allComments![indexPath.row].text
    commentCell.setMargin()

    return commentCell


所以,如果UILabel的frame变大than tableview.rowHeight,就不会出现了。因此,行高需要从heightForRowAtIndexPath(我假设)基于每个单元格调整大小

【问题讨论】:

heightForRowAtIndexPath 的调用完全独立于cellForRowAtIndexPath。表格视图需要知道它的完整高度,然后才需要显示此时恰好可见的少数单元格。 您是否使用自动调整单元格大小的自动布局?如果不是,您的 tableView:heightForRowAtIndexPath: 方法中的当前代码是什么?您是否看过类似以下的答案:***.com/q/20068849/558933、***.com/q/25180443/558933、***.com/q/27374612/558933 可以在每个单元格的基础上自动调整 UITableView 的单元格高度吗? 看到这个帖子:***.com/questions/18746929/… 【参考方案1】:

解决方案

我找到的解决方案有点粗糙,但我没有看到任何其他方法。基本上,除了要显示的文本字符串和字体属性之外,我知道我的 UILabel 的宽度将占据屏幕的多少比例。基于这些因素,我可以使用下面的代码来确定需要多少 UILabel 的高度,并且通过考虑默认单元格和标签高度可以在单元格本身生成之前找到我需要的单元格高度:

// width of string as a particular font in one line
func getStringSizeForFont(font: UIFont, myText: String) -> CGSize 
    let fontAttributes = [NSFontAttributeName: font]
    let size = (myText as NSString).sizeWithAttributes(fontAttributes)

    return size



func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat 
    // get the string size
    let stringSizeAsText: CGSize = getStringSizeForFont(UIFont(name: "Avenir", size: 16.0)!, myText: allComments![indexPath.row].text)

    // label width and height are hardcoded here, but can easily be made dynamic based on the screen width
    // and the constant proportion of the width the UILabel will take up
    // example:
    // let labelWidth: CGFloat = UIScreen.mainScreen().bounds.width * widthProportion
    // originalLabelHeight can be hardcoded or made dynamic; I'm going to leave my hardcoded because I'll be leaving the height
    // the same across all devices

    let labelWidth: CGFloat = 259.0
    let originalLabelHeight: CGFloat = 20.5

    // the label can only hold its width worth of text, so we can get the ammount of lines from a specific string this way
    let labelLines: CGFloat = CGFloat(ceil(Float(stringSizeAsText.width/labelWidth)))

    // each line will approximately take up the original label's height
    let height =  tableView.rowHeight - originalLabelHeight + CGFloat(labelLines*stringSizeAsText.height)

    return height


【讨论】:

stringSize.width/labelWidth 不返回确切的行数。如果您的换行模式是 .ByWordWrapping,则您的标签会在行尾产生一个空格。但是,计算的 stringSize 不考虑这些空格。当这些空格的宽度之和大于labelWidth时,则计算的行数应该是错误的。 你是对的,这是我偶尔遇到的问题。就我而言,它并没有造成太大的麻烦,但绝对是任何希望实施此解决方案的人都应该考虑的事情【参考方案2】:

1) 在我看来,对于您的这种情况,最好的选择是:

覆盖 func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat 返回 UITableViewAutomaticDimension

…并且在“heightForRowAtIndexPath”中表示“返回 UITableViewAutomaticDimension”

2) 如果您计算高度,另一种选择是:

在“cellForRowAtIndexPath”中,您应该能够更改“self.tableView.rowHeight”的值。 如果 TableView 有不同的 nib,此选项也很有用。

【讨论】:

这仅在单元格被完全约束时才有效;我正在以编程方式构建单元格,并且由于单元格的变化而无法在我的情况下设置约束。事后看来,最好从一堆不同的完全约束的 nib 加载单元,而不是尝试以编程方式制作一个灵活的单元以用于不同的情况。【参考方案3】:

动态调整表格视图单元格高度的最佳选择是使用自动布局。

将标签的底部设置为白色 UIView 的底部,并确保白色 UIView 具有左、右、上和下约束:)

Same type of example is explained here programmatically....

【讨论】:

【参考方案4】:

好吧,我完成了本教程,一行代码帮助我调整了 uitableview 单元格内容的大小,其中包含标签的内容。

//=====
https://www.raywenderlich.com/129059/self-sizing-table-view-cells
//=====

添加

tableView.rowHeight = UITableViewAutomaticDimension

tableView.estimatedRowHeight = 140

在 viewDidLoad() 中并确保 label/textview 的行数为 0

【讨论】:

这仅在单元格的视图受到完全约束时才有效,我是动态生成单元格的,因此这不适用于我的场景。你是对的,这在执行诸如从笔尖加载完全受约束的单元格之类的操作时会起作用

以上是关于在 iOS Swift 中动态调整 UITableViewCell 高度的主要内容,如果未能解决你的问题,请参考以下文章

Swift:动态 UIView 在调整大小后将 UIView 推到其下方

Swift uiview 在 uitable 之上

向上滚动 UITable 后的 Swift 5 调用函数

使用 IOS9 时,无法在 UITable 的部分标题上的 UIButtons 上触发触摸事件(IOS10 很好)

Swift - 在标签中动态调整大小后动态调整视图大小

在 UIScrollView (Swift) 中使用动态内容调整 UiView 高度