iOS7和iOS8中动态UITableViewCell高度不可能

3
我很难在iOS 7和8下实现动态大小单元格的表视图布局。我无法详细说明所有差异(即“损坏布局的方式”),因为这些差异可以使用不同的“调整”、“解决方法”和其他我在此处和其他地方找到的东西来产生。但最终,无论是在iOS 7还是iOS 8上(如果不是两者都有),某些或所有单元格内容都会对齐不良,因为布局系统“破坏”了一个自定义约束以“恢复”。
基本上,我有三种不同类型的内容。由于我不仅在上述表视图中显示这些内容,因此我将内容包装在三个UIView子类的中。让我们称它们为SummaryView。 对于表视图,我创建了三个UITableViewCell的子类,每个子类都向其contentView添加相应的SummaryView并设置self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight。在updateViewConstraints上,我通常会删除之前可能添加的所有“我的”约束,然后执行...
- (void)updateConstraints
{
   // ...removed custom constraints before
    NSDictionary *views = NSDictionaryOfVariableBindings(_summaryView);

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_summaryView]-|"
                                                                       options:0
                                                                       metrics:nil
                                                                         views:views]
                        toView:self.contentView];

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[_summaryView]-|"
                                                                       options:0
                                                                       metrics:nil
                                                                         views:views]
                        toView:self.contentView];
    [super updateConstraints];
}

tableView:estimatedHeightForRowAtIndexPath:方法中,我会根据内容类型返回一个静态的预估高度。在tableView:heightForRowAtIndexPath:方法中,我使用“原型”单元格的方式来...
// ..set content on prototype cell before
[prototypeCell setNeedsUpdateConstraints];
[prototypeCell layoutIfNeeded];
CGSize size = [prototypeCell systemLayoutSizeFittingSize:UILayoutFittingExpandedSize];
return size.height;

通常情况下,调试器会在UIViewAlertForUnsatisfiableConstraints(即[prototypeCell layoutIfNeeded])处中断,因为<NSLayoutConstraint:0x17409f180 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x17019f070(44)]>与其他约束冲突。

所以我尝试了...

  • Setting tableView.rowHeight = UITableViewAutomaticDimension in table view's viewDidLoad
  • Not implementing tableView:estimatedHeightForRowAtIndexPath:
  • Using...

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) {
        cell.contentView.frame = cell.bounds;
        cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    }
    

    ...before applying cell's content and calculating its layout

  • Doing self.contentView.bounds = CGRectMake(0.0, 0.0, 1000, 1000); upon cell initialization
  • Probably other things which, right now, I can't remember as I'm on this for more or less two days now.
一直以来,如果我找到了一个不会抱怨不满足的限制条件的变体,布局通常仍然会混乱。虽然我使表视图不设置“默认”<NSLayoutConstraint:0x17409f180 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x17019f070(44)]>约束,但是基于tableView:heightForRowAtIndexPath:返回的高度而设置的约束集也不能适应布局系统。
这篇文章有点绝望。我知道如果没有具体视图类的约束条件,你无法重新检查它们。但是,由于我可以在没有问题的情况下使用这些视图,因此不应该是子视图约束问题。
我想我将不得不手动计算所有视图元素的大小(基于[UIScreen mainScreen].bounds),并将它们直接设置为所有子视图的宽度和高度。这样,我就能够获得单元格的具体高度,并手动设置contentView的框架。这相当可惜,因为它会显着破坏布局代码。
此致 敬礼, Gabriel

1
你走错了路,你的单元格高度基于通过 –tableView:heightForRowAtIndexPath: 方法返回的值。这将定义屏幕上的实际高度。玩弄自动布局或自动调整大小掩码是无济于事的。 - holex
2
@holex 你的狩猎声望? A:请随意告诉我(和其他人)正确的路径是什么。 B:我认为从来没有人怀疑过tableView:heightForRowAtIndexPath的用途。 C:我不是在玩弄自动布局,我实际上正在使用它。 D:与autoresizingMask玩耍似乎已经帮助了其他人:https://dev59.com/LGIk5IYBdhLWcg3wl_Ip#19154287。 - dergab
2个回答

2
最终我找到了一个可接受的解决方案,它不会使布局代码混乱。 简而言之: 在表格视图单元格的 updateConstraints 中,我将从_summaryView-底部到父视图底部的约束删除。因此,内容视图上的高度约束不会干扰摘要视图上的高度约束。
- (void)updateConstraints
{
    NSDictionary *views = NSDictionaryOfVariableBindings(_summaryView);

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_summaryView]-|"
                                                                       options:0
                                                                       metrics:nil
                                                                         views:views]
                        toView:self.contentView];

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[_summaryView]"
                                                                       options:0
                                                                       metrics:nil
                                                                         views:views]
                        toView:self.contentView];
    [super updateConstraints];
}

tableView:heightForRowAtIndexPath:中,我只是使用所涉及的单元格的intrinsicContentSize来获取高度:
        [prototypeCell.summaryView applyStuff];
        [prototypeCell layoutIfNeeded];
        height = [prototypeCell intrinsicContentSize].height;

所提到的表视图单元格的intrinsicContentSize实现如下:
- (CGSize)intrinsicContentSize
{
    // Calculate the available content width if not done yet
    static CGFloat availableWidth = 0.0;
    if (availableWidth == 0.0) {
        availableWidth = CGRectGetWidth([UIScreen mainScreen].bounds);
    }

    // Check if the contentView's frame needs an update
    if (CGRectGetWidth(self.contentView.frame) != availableWidth) {
        CGRect frame = CGRectMake(0.0, 0.0, availableWidth, 100.0);
        self.contentView.frame = frame;
    }

    [_summaryView layoutIfNeeded];
    CGSize size = _summaryView.frame.size;
    size.height += 2.0 * V_PADDING;
    size.width += 2.0 * H_PADDING;
    return size;
}

请注意,对于支持纵向和横向模式的应用程序,availableWidth必须在方向更改时重置,或者它不应该是static。* _PADDING是我希望_summaryView在所有侧面都具有的空间。
此外,我从单元格的初始化代码中删除了self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth,因为它似乎没有任何明显的影响。

我理解你的沮丧,我也在使用类似的方法:没有设置尾部约束,并通过将self.view.frame.width(由于“UIView-Encapsulated-Layout-Width”,tableViewCell始终为零)应用于子视图的框架来计算其高度... 希望看到是否有人能提出一种方法,使contentView框架完全由intrinsicContentSize计算,似乎仍然没有可能的解决方案。 - vk.edward.li
我的tableView单元格有一个很大的UILabel,在tableView:heightForRowAtIndexPath:中,我使用contentLabelHeight = contentLabel.font.sizeOfString(contentLabel.text!, constrainedToWidth: Double(self.view.frame.width - 44)).height首先计算高度(p.s. 44是你的2.0 * H_PADDING),然后返回contentLabelHeight + contentLabel.frame.origin.y + 20(p.s. 20是你的2.0 * V_PADDING)。 - vk.edward.li
是的,在iOS 7下似乎没有处理“动态行高”的好方法。 - dergab

0

我可能有点晚加入讨论,但我遇到了类似的问题并找到了解决方案。

在添加约束时,请将它们添加到contentView而不是cell中。否则,视觉语言格式中的“| -”和“- |”将创建与单元格本身相关的约束,而我们可怜的contentView将没有任何约束。

因此,不要使用[self addConstraints:...];,而要使用[self.contentView addConstraints:...];


_summaryView 是单元格的 contentView 的子视图。当向视图添加约束时,无论约束是针对此视图本身还是其子视图,都没有关系。 - dergab
我刚刚仔细检查了一下,确保它在我的当前项目中有所不同 - 确实如此。任何带有“| -”或“- |”的视觉语言约束都是指容器接收addConstraint方法。我还收到消息“检测到约束模糊地建议表格视图单元格内容视图的高度为零的情况”,如果我将“addConstraint”发送到单元格而不是内容视图,则不会出现此问题。在iOS8.3下。 - Jean Le Moignan
好的,这是一个解释。不幸的是,我目前没有时间在我们的项目中测试它。但我会记住它,迟早会检查它... - dergab

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