在iOS Swift中动态调整UITableViewCell高度

4
我正在尝试创建一个UITableView,其中单元格的高度根据所显示的文本量而定。这似乎很简单,因为可以使用heightForRowAtIndexPath并使用下面所示的代码获取UILabel所需的大小。然而,此方法在调用cellForRowAtIndexPath之前被调用,因此我无法根据单元格中的内容进行调整大小,这对我来说似乎是一个非常奇怪的选择。基于内容动态调整单元格高度的“最佳实践”是什么?
此外,为什么苹果没有让heightForRowAtIndexPath方法在cellForRowAtIndexPath之后调用?
我在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 的框架变成大于 tableview.rowHeight,则它将不会显示。因此,需要在每个单元格的基础上从 heightForRowAtIndexPath(我假设)重新调整行高。

heightForRowAtIndexPath方法完全独立于cellForRowAtIndexPath方法的调用。在需要显示当前可见的少数单元格之前,表视图需要提前知道其完整的高度。 - rmaddy
1
你是否正在使用自动布局来自动调整单元格的大小?如果没有,那么你在 tableView:heightForRowAtIndexPath: 方法中的当前代码是什么?你是否看过以下答案:https://dev59.com/MGIj5IYBdhLWcg3wnmXA,https://dev59.com/1l8e5IYBdhLWcg3w9-Rs,https://dev59.com/o14c5IYBdhLWcg3w7t_E? - Robotic Cat
UITableView能否根据每个单元格自动调整自动布局的高度? - treyhakanson
请查看此帖子:https://dev59.com/4WMl5IYBdhLWcg3wCDGW - koen
4个回答

2

解决方案

我找到的解决方案有点复杂,但我没有看到其他方法。基本上,我知道我的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

}

2
stringSize.width/labelWidth并不返回准确的行数。如果您的分行模式是.ByWordWrapping,那么您的标签会在行末产生一个空白。然而,计算得到的字符串大小并没有考虑这些空白。 当这些空白的宽度总和大于labelWidth时,计算出的行数就会有误。 - Mark
你说得对,这是我偶尔遇到的问题。对于我的目的来说,它并没有造成太多麻烦,但任何想要实现这个解决方案的人都应该考虑这个问题。 - treyhakanson

1

1) 在我看来,你的情况最好的选择是:

override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { return UITableViewAutomaticDimension }

... 并在“heightForRowAtIndexPath”中指示“return UITableViewAutomaticDimension”

2) 如果你计算高度,另一个选项是:

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


只有在单元格完全受限制时才能起作用;在我的情况下,我是以编程方式构建单元格的,由于单元格的变化,无法设置约束条件。事后看来,最好从一堆不同的完全受限制的nibs中加载单元格,而不是尝试以编程方式创建一个灵活的单元格以在不同情况下使用。 - treyhakanson

0

-2

我经历了这个教程,一行代码帮助我调整uitableview单元格内容,使其与其中的标签内容大小相适应。

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

添加

tableView.rowHeight = UITableViewAutomaticDimension

tableView.estimatedRowHeight = 140

在viewDidLoad()中,确保标签/文本视图的行数为0。

1
这仅适用于单元格视图完全受限制的情况下,我正在动态生成单元格,因此对于我的情况无法使用此方法。您是正确的,当像从nib加载完全受限制的单元格时,这将起作用。 - treyhakanson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接