【问题标题】:UILabel in UITableViewCell with auto layout has wrong height具有自动布局的 UITableViewCell 中的 UILabel 高度错误
【发布时间】:2014-07-03 01:44:49
【问题描述】:

我有一个UITableView,其中单元格的固定高度为 100 磅。这些单元格是在一个 xib 文件中创建的,该文件使用 3 个约束将UILabel 固定到单元格contentView 的左、右和上边缘。标签的垂直拥抱优先级设置为 1000,因为我希望单元格的高度尽可能小。

当 xib 文件中单元格的宽度设置为 320 磅时,与 iPhone 上的 tableView 的宽度相同,自动布局按预期工作。但是,当我将单元格的宽度设置为小于 320 磅时,我得到了意想不到的结果。 (我想在具有不同宽度的 tableViews 中使用相同的单元格,例如在通用应用程序中)

例如:当我将宽度设置为 224 磅并给标签一个在该宽度上占用 2 行的文本时,标签的高度将增加以适应 2 行,但是当单元格随后调整为 320 磅时为了适应该宽度的 tableView,文本仅占 1 行,但标签的高度保持在 2 行。

我已经在 GitHub 上放了一个示例项目来演示这个问题:https://github.com/bluecrowbar/CellLayout

有没有办法让UILabel 始终调整大小以包含其文本内容?

【问题讨论】:

  • 在标签上设置preferredMaxLayoutWidth似乎可行,但我不确定在哪里计算和设置它的值。

标签: ios uitableview autolayout


【解决方案1】:

在单元格子类中添加这个有效:

- (void)layoutSubviews
{
    [super layoutSubviews];
    [self.contentView layoutIfNeeded];
    self.myLabel.preferredMaxLayoutWidth = self.myLabel.frame.size.width;
}

我在http://useyourloaf.com/blog/2014/02/14/table-view-cells-with-varying-row-heights.html 上找到了这个。

更新 1: 这个答案适用于 iOS 7。我发现自 iOS 8 以来,表格视图单元格中的自动布局非常不可靠,即使对于非常简单的布局也是如此。经过大量的实验,我(大部分)回到手动布局和手动计算单元格的高度。

更新 2: 我在 iOS 9 上运行了一些测试,似乎 UITableViewAutomaticDimension 终于像宣传的那样工作了。耶!

【讨论】:

  • 很好的发现。声明[self.contentView layoutIfNeeded] 是我在这条道路上陷入困境的地方。我假设[super layoutSubviews] 称为layoutSubviews,但显然不是。这当然解释了为什么标签的框架在[super layoutSubviews] 之后没有立即更新。
  • 太棒了。在花了几个小时试图弄清楚这一点后,我真的要放弃自动布局(tableViewCell 上的动态大小标签和 cell.contentView.bounds.size.height 太小或太大 -> 发生在任何大于320 像素宽)。谢谢。
  • 是UILabel的bug?
  • 我最初的答案是针对 iOS 7。我发现自 iOS 8 以来,表格视图单元格中的自动布局非常不可靠,即使对于非常简单的布局也是如此。经过大量的实验,我(大部分)回到手动布局和手动计算单元格的高度。
  • 对我不起作用,@Bimawa 是的,它似乎是 UILabel 问题,因为 contentView 宽度看起来很完美,但标签一个不正确。
【解决方案2】:

愚蠢的错误!我在这个问题上几乎迷失了一天,最后我用 Steven Vandewghe 的解决方案解决了它。

Swift 版本:

override func layoutSubviews() {
    super.layoutSubviews()
    self.contentView.layoutIfNeeded()
    self.myLabel.preferredMaxLayoutWidth = self.myLabel.frame.size.width
}

【讨论】:

  • 这里也一样。我浪费了几个小时。但发现上面的工作。唯一的问题是为什么我们需要 self.contentView.layoutIfNeeded() 来获得正确的尺寸?
【解决方案3】:

由于您限制标签的 widthintrinsicContentSize 尊重 width 并调整 height。这就产生了先有鸡还是先有蛋的问题:

  1. 单元格的自动布局结果取决于标签的intrinsicContentSize
  2. 标签的intrinsicContentSize 取决于标签的width
  3. 标签的width 取决于单元格的自动布局结果

所以发生的情况是单元格的布局仅计算一次,其中 (2) 基于 XIB 文件中的静态宽度,这导致错误的标签 height

你可以通过迭代来解决这个问题。即,在第一次计算设置标签的width 后,重复自动布局计算。在您的自定义单元格中这样的东西会起作用:

- (void)drawRect:(CGRect)rect
{
    CGSize size = self.myLabel.bounds.size;
    // tell the label to size itself based on the current width
    [self.myLabel sizeToFit];
    if (!CGSizeEqualToSize(size, self.myLabel.bounds.size)) {
        [self setNeedsUpdateConstraints];
        [self updateConstraintsIfNeeded];
    }
    [super drawRect:rect];
}

原始解决方案无法可靠运行:

- (void)layoutSubviews
{
    [super layoutSubviews];
    // check for need to re-evaluate constraints on next run loop
    // cycle after the layout has been finalized
    dispatch_async(dispatch_get_main_queue(), ^{
        CGSize size = self.myLabel.bounds.size;
        // tell the label to size itself based on the current width
        [self.myLabel sizeToFit];
        if (!CGSizeEqualToSize(size, self.myLabel.bounds.size)) {
            [self setNeedsUpdateConstraints];
            [self updateConstraintsIfNeeded];
        }
    });
}

【讨论】:

  • 这种方式的问题是可以看到重新布局。尝试在 iPad 模拟器中运行应用程序,看看我的意思。
  • @StevenVandeweghe 嗯。它仅在 iPad Retina 模拟器中执行此操作。其他模拟器工作。可能是模拟器错误。
  • 更新了答案。将代码移动到drawRect 可以避免dispatch_async 并且据我所知可以可靠地工作。
  • 我没想到drawRect:。这也适用于drawRect[super drawRect:rect]; self.myLabel.preferredMaxLayoutWidth = self.myLabel.frame.size.width;。无论如何,我已经打开了一个 DTS,只是为了确定 Apple 认为的最佳解决方案。
  • @TimothyMoose 谢谢。很好的解释。
【解决方案4】:

我在 iOS 12 中使用 XCode 10,但我仍然遇到自动布局问题,当表格首次呈现时,单元格没有被赋予正确的高度。 Timothy Moose 的回答并没有为我解决问题,但根据他的解释,我想出了一个对我有用的解决方案。

我继承 UITableViewController 并覆盖 viewDidLayoutSubviews 消息以检查宽度变化,然后如果宽度发生变化则强制更新表格。这在视图呈现之前解决了问题,这使它看起来比我的其他努力更好。

首先在你的自定义 UITableViewController 子类中添加一个属性来跟踪之前的宽度:

@property (nonatomic) CGFloat previousWidth;

然后覆盖 viewDidLayoutSubviews 以检查宽度变化:

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    CGFloat width = self.view.frame.size.width;
    if (self.previousWidth != width) {
        self.previousWidth = width;
        [self.tableView beginUpdates];
        [self.tableView endUpdates];
    }
}

这解决了我的表格单元格最初有时被赋予错误高度的问题。

【讨论】:

    【解决方案5】:

    我知道这是一个老问题,但也许这个 UILabel 子类也可以帮助一些人:

    class AutoSizeLabel: UILabel {
        override var bounds: CGRect {
            didSet {
                if bounds.size.width != oldValue.size.width {
                    self.setNeedsUpdateConstraints()
                }
            }
        }
    
        override func updateConstraints() {
            if self.preferredMaxLayoutWidth != self.bounds.size.width {
                self.preferredMaxLayoutWidth = self.bounds.size.width
            }
            super.updateConstraints()
        }
    }
    

    注意:也适用于您的 UILabel 在 StackView 内无法正确调整自身大小的情况

    【讨论】:

      【解决方案6】:

      我通常将这两行添加到 viewDidLoad()

          self.tableView.rowHeight = UITableViewAutomaticDimension
          self.tableView.estimatedRowHeight = 96
      

      这将自动调整单元格的大小

      【讨论】:

        猜你喜欢
        • 2021-10-12
        • 1970-01-01
        • 1970-01-01
        • 2017-04-07
        • 1970-01-01
        • 2014-02-11
        • 2012-10-18
        • 2014-01-03
        • 1970-01-01
        相关资源
        最近更新 更多