iOS8 中的表格视图单元自动布局

4

我似乎无法让自动布局在我的表视图单元格上工作。

在某些单元格上,它似乎可以正常工作,而在其他单元格上,它似乎无法正常工作。即使这些单元格完全相同。

例如,在某些单元格中,“描述”将会有多行文本,并且它会正确地工作……

……但是在其他单元格中,“描述”将会有多行文本,但只显示其中的一行,其余都是空白。

你能帮我找出我缺少或做错了什么吗?谢谢!

我作为第一次尝试者,使用这个StackOverflow问题来指导我的过程:在UITableView中使用自动布局实现动态单元格布局和可变行高度

1. 设置和添加约束

我认为这些基本上都很好地工作。

2. 确定唯一的表视图单元格重用标识符

我不确定是否需要担心这一部分,因为我始终会有一个标题、时间和描述。

iOS 8 - 自适应行高度的单元格 3. 启用行高度估算

我将其添加到viewDidLoad中:

self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 180.0;

更新:按Acey的请求添加更多信息 enter image description here

明确一下,我设置了以下限制:

  • 标题:左侧15,顶部85,右侧15
  • 标题和时间之间的垂直间距为10
  • 时间和描述之间的垂直间距为10
  • 我使用Cmd点击这三个标签,并添加了Leading Edges和Trailing Edges
  • 我将20个像素钉在描述和表视图单元格底部之间

更新2:问题已解决 下面的答案非常有效,但请注意,额外的间隔是由于设置了单元格的高度过大,因此Xcode自动添加额外的空间以填补表视图单元格的高度,因为文本标签没有填满表视图单元格的完整高度。

如果您遇到同样的问题并需要帮助,请告诉我。

谢谢大家!


如果您想要针对 iOS 8 及以上版本进行开发,并且可以正确设置约束条件,那么您可以省略 tableView:heightForRowAtIndexPath: 方法。看起来您可能在创建约束方面遇到了问题。您能否在 IB 中从左侧截图并展示所有单元格的约束条件?(我只能推测这是您需要帮助的区域 - 您并没有提出任何具体问题)。 - Acey
谢谢您的回复!我在底部更新了我的问题,展示了您想要查看的屏幕截图。请告诉我那是否是您要找的内容。我的主要问题是,我正在尝试根据内容调整此单元格的大小,但它没有起作用,所以您能帮我找出我缺少什么吗?谢谢! - Realinstomp
@Acey,我发布的截图有帮助吗?感谢你的帮助! - Realinstomp
@Reez:你介意使用自动布局吗?我经常与单元格一起工作,也遇到了自动布局的问题,所以我只是将它们变成了动态的(使用self.view.bounds)。如果你不介意使用自动布局,我可以在下面发布一个答案。 - Chris
@Chris 确定没问题! - Realinstomp
2个回答

3

我还没有尝试使用新的iOS 8机制。但是在iOS 6 / 7时,我遇到了类似的问题。将应用程序更新到iOS 8后,它仍然正常工作,所以也许旧方法仍然是最好的方法?

我这里有一些代码示例:

AutoLayout多行UILabel截断部分文本

在横向和iPad上使用AutoLayout UITableView,基于iPhone竖屏计算高度

简而言之,在iOS 8之前的方法涉及在tableView:heightForRowAtIndexPath:中保留一个单元格副本,仅用于计算高度。但是这对于处理多行UILabel不足够。我必须子类化UILabel以在每次调用layoutSubviews时更新preferredMaxLayoutWidth

preferredMaxLayoutWidth属性似乎是我缺失的秘密武器。一旦设置好这个属性,我的大多数单元格都可以完美地工作。
我遇到的第二个问题只需要正确设置内容压缩阻力和内容吸附属性即可。例如,告诉标签吸附文本将意味着它不会扩展以填充空白区域,从而导致单元格收缩。
一旦我做到了这两件事,我的单元格现在可以处理任何字体大小或任何数量的文本,而无需任何混乱的布局代码。学习曲线很陡峭,但最终我认为这很值得,因为我的应用中有很多动态内容。
编辑
在遇到iOS 8的一些问题后,我添加了一些详细信息来解决这些非常奇怪的自动布局错误。
使用我提到的代码时,当未将单元格“行高”设置为自定义时,它似乎无法正常工作。在IB中通过选择单元格并单击autoLayout选项卡(其中包含所有内容压缩阻力设置等)找到此设置。勾选复选框,它将填充一个临时高度。
第二个问题是,我在代码中保留了一个单元格的本地副本,并在heightForRowAtIndexPath:方法中多次重复使用它。这似乎会导致每次调用时单元格高度大幅增加。我需要通过调用以下方法重新初始化本地副本:
localCopy = [self.tableView dequeueReusableCellWithIdentifier:@"mycell"];

看起来新的Xcode 6 / iOS 8的变化与iOS 7非常不兼容,管理方式似乎有很大不同。
希望这可以帮到你。
编辑2
在阅读了这篇文章后:iOS AutoLayout multi-line UILabel 我又遇到了另一个关于iOS 7 / iOS 8自动布局支持的问题!!! 我重写了layoutSubviews,对于iOS 8,我还需要重写setBounds以在调用super之后更新preferredMaxLayoutWidth。苹果修改了什么鬼!
似乎这是IB中preferredMaxLayoutWidth的设置问题,因为iOS 7无法使用自动特性,如果您在多个设备上使用相同的UILabel,则只会使用1个宽度。因此,在iOS 8平板电脑上,UITableViewCell会更大,因为相同的单元格在iOS 8 iPhone上需要有2行。

太棒了,谢谢您的指导,我会尝试实现它! - Realinstomp
@Reez没问题,告诉我进展如何,我正在关注那份赏金;-) - Simon McLoughlin

1

这是我的尝试。

您可以创建一个获取所需单元格视图的方法/函数。就像这样:

- (UIView *) getCellView {

    UIView *cellView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, 0.0f)];
    cellView.tag = 1;
    cellView.backgroundColor = [UIColor clearColor];

    UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(15.0f, 10.0f, 15.0f, 15.0f)]; //I assumed that was the size of your imageView;
    imgView.image = [UIImage imageNamed:@"whatever-your-image-is-called"];
    [cellView addSubview:imgView];

    CGFloat xPadding = 15.0f;
    CGFloat yPadding = 15.0f;
    UILabel *headlineLabel = [[UILabel alloc ]initWithFrame:CGRectMake(xPadding, 0.0f, self.view.frame.size.width - (xPadding*2), 0.0f)];
    headlineLabel.numberOfLines = 0;
    headlineLabel.text = @"Red Sox season fell apart after World Series title (The Associated Press)";
    [headlineLabel sizeToFit];
    CGRect hFrame = headlineLabel.frame;
    if(hFrame.size.width > self.view.frame.size.width - 31.0f)
        hFrame.size.width = self.view.frame.size.width - 30.0f;
    hFrame.origin.y = imgView.frame.size.height + yPadding;
    headlineLabel.frame = hFrame;

    UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(xPadding, 0.0f, self.view.frame.size.height-(xPadding*2), 0.0f)];
    timeLabel.text = @"4h";
    //timeLabel.numberOfLines = 0; //uncomment if it will wrap on multiple lines;
    [timeLabel sizeToFit];
    hFrame = timeLabel.frame;
    if(hFrame.size.width > self.view.frame.size.width - 31.0f)
        hFrame.size.width = self.view.frame.size.width - 30.0f;
    hFrame.origin.y = headlineLabel.frame.size.height + yPadding;
    timeLabel.frame = hFrame;

    UILabel *descriptLabel = [[UILabel alloc] initWithFrame:CGRectMake(xPadding, 0.0f, self.view.frame.size.height - (xPadding*2), 0.0f)];
    descriptLabel.text = @"Boston (AP) -- To Boston Red Sox manager John Farrel, it hardly seems possible that just 11 months ago his team was celebrating the World Series championship on the field at Fenway Park.";
    descriptLabel.numberOfLines = 0; //I would suggest something like 4 or 5 if the description string vary from 1 line to more than 5 lines.
    [descriptLabel sizeToFit];
    hFrame = descriptLabel.frame;
    if(hFrame.size.width > self.view.frame.size.width - 31.0f)
        hFrame.size.width = self.view.frame.size.width - 30.0f;
    hFrame.origin.y = timeLabel.frame.size.height + yPadding;
    descriptLabel.frame = hFrame;

    cellView.frame = CGRectMake(0.0f, 0.0f, self.view.frame.size.width, descriptLabel.frame.origin.y + descriptLabel.frame.size.height + 15.0f /*some padding*/);
    return cellView;
}

如果您正在使用indexPath.row,您只需更改方法名称为- (UIView *)getCellView:(NSIndex) *indexPath,它应该可以正常工作。
然后在您的heightForRowAtIndexPath中,您可以执行以下操作: return [[self getCellView] frame].size.height; 或者 return [[self getCellView:indexPath] frame].size.height 在您的cellForRowAtIndexPath中,您只需执行以下操作:
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil) {

    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    cell.accessoryType = UITableViewCellAccessoryNone;
    cell.textLabel.text = @"";
    cell.detailTextLabel.text = @"";
}
[[cell viewWithTag:1] removeFromSuperview];
[cell addSubview:[self getCellView]; //or [cell addSubview:[self getCellView:indexPath]];
return cell;

希望这可以帮到你,如果有任何不清楚或不能正常工作的地方,请告诉我。在cellForRowAtIndexPath里可能需要一些调整以适应你的用法,但这应该是你需要开始的全部内容了。编码愉快!

谢谢,我会试一下! - Realinstomp
你是用这个来处理iOS7和iOS8吗?只是好奇,因为我只担心iOS8(尽管我肯定这对iOS8有效)。 - Realinstomp
针对iOS8和iOS7,对于像iPhone 6和iPhone 6 Plus这样的大屏手机,很可能需要更大的图像和更大的文本。因此,我添加了一些全局变量,用于动态适配特定设备的文本/字体和矩形大小。由于使用了sizetofit,上述代码应该可以处理任何字体大小。 - Chris

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