使用 Autolayout 自定义具有动态高度和全宽图像的 TableViewCell

Posted

技术标签:

【中文标题】使用 Autolayout 自定义具有动态高度和全宽图像的 TableViewCell【英文标题】:Custom TableViewCell with dynamic height and full width image using Autolayout 【发布时间】:2015-07-28 16:40:15 【问题描述】:

我真的被自动布局和自定义 tableviewcell 卡住了。

我希望我的 tableviewcell 具有动态高度。表格单元格应包含与表格视图单元格宽度相同的图像,高度应根据纵横比 0.6 进行调整

This is what it should look like

But this is what I get

为了让您清楚地了解发生了什么,我为 tableViewCell 及其子视图提供了一些漂亮的背景颜色……

绿色 = 缩略图视图 蓝色 = 自我 红色 = 内容视图 紫色 = 热点名称标签 黄色 = 类别标签

这是我的代码的样子:

初始化器:

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) 

    self.contentView.backgroundColor = [UIColor redColor];
    self.backgroundColor = [UIColor blueColor];

    CGFloat imageAspectRatio = 720/432;
    CGFloat imageHeight = self.contentView.bounds.size.width / imageAspectRatio;

    CGRect imageFrame = CGRectMake(0, 0, self.contentView.bounds.size.width,imageHeight);

    self.thumbnailView = [[UIImageView alloc] initWithFrame:imageFrame];
    [self.thumbnailView setBackgroundColor:[UIColor greenColor]];
    [self.thumbnailView setContentMode:UIViewContentModeScaleAspectFill];
    [self.thumbnailView.layer setMasksToBounds:true];
    [self.contentView addSubview:self.thumbnailView];

    self.hotspotNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(40, 15, self.contentView.bounds.size.width - 80, 20)];
    [self.hotspotNameLabel setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
    [self.hotspotNameLabel setTextColor:[UIColor colorWithWhite:(32/255.0) alpha:1]];
    [self.hotspotNameLabel setLineBreakMode:NSLineBreakByWordWrapping];
    [self.hotspotNameLabel setNumberOfLines:0]; // Unlimited Lines
    [self.hotspotNameLabel setBackgroundColor:[UIColor purpleColor]];
    [self.hotspotNameLabel setFont:[UIFont fontWithName:@"AvenirNext-Medium" size:20]];

    [self.contentView addSubview:self.hotspotNameLabel];

    // Category Label
    self.categoryLabel = [[CategoryLabel alloc] initWithFrame:CGRectMake(40, 15, self.contentView.bounds.size.width - 80, 10)
                                                   categoryId:22 categoryName:@"Food"];
    [self.contentView addSubview:self.categoryLabel];

    // Constraints

    [self.contentView setTranslatesAutoresizingMaskIntoConstraints:false];
    [self.hotspotNameLabel setTranslatesAutoresizingMaskIntoConstraints:false]; // Enables autosizing
    [self.categoryLabel setTranslatesAutoresizingMaskIntoConstraints:false];
    [self.thumbnailView setTranslatesAutoresizingMaskIntoConstraints:false];

    [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.thumbnailView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem: self.contentView attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0]];
    [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.thumbnailView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem: self.contentView attribute:NSLayoutAttributeWidth multiplier:0.6f constant:0]];

    [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[nameLabel]-0-|" options:0 metrics:nil views:@@"nameLabel": self.hotspotNameLabel]];
    [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[thumbnail]-20-[nameLabel]-10-[categoryLabel]-0-|" options:0 metrics:nil views:@@"thumbnail": self.thumbnailView,@"nameLabel": self.hotspotNameLabel, @"categoryLabel":self.categoryLabel]];



return self;

布局子视图:

-(void)layoutSubviews 

[super layoutSubviews];

[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];


self.hotspotNameLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.hotspotNameLabel.bounds);

tableView:HeightForRowAtIndexPath

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
    return [self heightForLargeCellAtIndexPath:indexPath];


- (CGFloat)heightForLargeCellAtIndexPath:(NSIndexPath *)indexPath 

    LargeHotspotCell *sizingCell = [[LargeHotspotCell alloc] init];

    [self configureLargeCell:sizingCell atIndexPath:indexPath];

    [sizingCell setNeedsLayout];
    [sizingCell layoutIfNeeded];

    CGFloat height = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    return height + 1 ;

【问题讨论】:

是否需要通过代码添加约束?在情节提要中做起来要容易得多 @NeilGaliaskarov 我也试过了,但我也无法用故事板完成它:s 我可以在这里发布一些带有基本概念的示例项目。你能提供你的 heightForRowAtIndexPath 方法实现吗 @NeilGaliaskarov 我将它添加到我的问题中:-) 请检查我的答案,如果它解决了您的问题,请不要忘记将其标记为已接受。 【参考方案1】:

这是我解释自动布局基本概念的小教程。 您的第一步,应该是子类化UITableviewCell,我将我的新单元命名为DynamicTableViewCell 并在身份检查器中分配它。

我将 UILabel 和 UIImageView 拖放到 contentView 中。 在带有 UILabel 约束的屏幕截图下方,我同时添加了前导和尾随空格约束,以使我的标签等于 contentView 宽度。它与单元格的底部对齐。

现在,是时候为 UIImageView 分配约束了。我正在添加纵横比 0.6 (imageHeight/imageWidth = 0.6) 并向 contentView 添加前导和尾随空格。随意使用常量来实现您想要的目的。

iphone5、iphone6模拟器中的最终结果

您的图像高度(绿色)在 5s 屏幕中为 60pt,在 6 屏幕中为 70pt,并且宽度调整正确:

为了您更好的理解,我发布Working Sample Project on github

【讨论】:

根据您对 iPhone5 与 iPhone6 的屏幕截图,我可以看到图像已正确缩放,但 tableviewcells 的高度没有改变。下面的标签可以有多行(因此这也应该影响单元格的高度)并且应该与图像的底部对齐。我认为您的标签现在显示在图像上方。 :-/ 我使所有单元格的单元格高度相等,您可以进一步自定义。 是的,但我已经能够做到这一点,但是我无法让文本显示在图像下方……我想将标签放置在图像下方的那一刻:[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[thumbnail]-[nameLabel]-[categoryLabel]-10-|" options:0 metrics:nil views:@@"thumbnail": self.thumbnailView,@"nameLabel": self.hotspotNameLabel,@"categoryLabel": self.categoryLabel]]; then它看起来不错,但我收到以下错误(请参阅下一条评论) 015-07-29 09:19:41.992 appName[6760:1818580] 无法同时满足约束。可能以下列表中的至少一个约束是您不想要的......

以上是关于使用 Autolayout 自定义具有动态高度和全宽图像的 TableViewCell的主要内容,如果未能解决你的问题,请参考以下文章

AutoLayout 在地图标注中动态调整 UILabel 高度和宽度?

Autolayout - 当一个视图具有动态高度时,在 UITableViewCell 中垂直居中两个视图

iOS UITableView+FDTemplateLayoutCell 配合AutoLayout分分钟教你实现动态高度自适应

使用 Auto Layout 创建具有多个不同自定义单元格的 UITableView 具有几乎相同的子视图

在具有动态高度的 IB uitableviewcell 中使用带有 XIB 的自定义视图

具有自动布局的自定义单元格的动态高度