需求背景

1、UITableViewCell 嵌套 UICollectionView
2、UICollectionViewCell 宽度根据文字长度变化(宽度不固定)
3、UITableViewCell 根据 UICollectionView 内容实现高度自适应(UICollectionView 不可滚动)

需求痛点

UICollectionViewCell 宽度根据文字自适应,因此无法确定一行能容纳几个 UICollectionViewCell

效果实现

初始化 UICollectionView

UICollectionViewFlowLayout * layout = [[UICollectionViewFlowLayout alloc] init];
// 1、使用 Autolayout 一定要给 Item 预估个宽高,否则无法自适应宽度
// 2、Item 宽度不能超出实际 UICollectionView 宽度,否则 UICollectionView 高度计算异常
layout.estimatedItemSize = CGSizeMake(100, 22);

UICollectionView * coll = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
coll.backgroundColor = [UIColor whiteColor];
coll.delegate = self;
coll.dataSource = self;
[coll registerClass:[CustomTableViewCollCell class] forCellWithReuseIdentifier:[CustomTableViewCollCell identifier]];

// 调用私有方法设置居左,否则当只有一个 item 的时候会居中显示,调用该方法不影响审核
SEL sel = NSSelectorFromString(@"_setRowAlignmentsOptions:");
if ([coll.collectionViewLayout respondsToSelector:sel]) {
    ((void(*)(id, SEL, NSDictionary*)) objc_msgSend)(coll.collectionViewLayout, sel, @{@"UIFlowLayoutCommonRowHorizontalAlignmentKey": @(NSTextAlignmentLeft), @"UIFlowLayoutLastRowHorizontalAlignmentKey": @(NSTextAlignmentLeft), @"UIFlowLayoutRowVerticalAlignmentKey": @(NSTextAlignmentLeft)});
}
[self.contentView addSubview:coll];
_coll = coll;

// 这里不设置 UICollectionView 高度
[coll mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.equalTo(abnormalTitleLabel.mas_right).offset(20);
    make.right.equalTo(self.contentView).offset(-15);
    make.top.equalTo(abnormalTitleLabel.mas_top);
    make.bottom.equalTo(self.contentView).offset(-20);
}];

UICollectionViewCell 重写 preferredLayoutAttributesFittingAttributes: 方法

- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {

    // 这两个不能少
    [self setNeedsLayout];
    [self layoutIfNeeded];

    UICollectionViewLayoutAttributes * attributes = [super preferredLayoutAttributesFittingAttributes:layoutAttributes];

    // 在这里计算文字的宽度
    CGSize size = [@"我是用来计算文字宽度的" boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, 17) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12]} context:nil].size;

    CGRect cellFrame = attributes.frame;
    // size.width 只是文字的宽度,要加上其他组件的宽和间距,这样才得到正确的 UICollectionViewCell 宽度
    cellFrame.size.width = size.width + 30;
    attributes.frame = cellFrame;

    return attributes;
}

不要重写 collectionView:layout:sizeForItemAtIndexPath: 方法

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    // 不要重写这个方法
}

UITableViewCell 重写 systemLayoutSizeFittingSize:withHorizontalFittingPriority:horizontalFittingPriority: 方法 iOS8及之后版本可用

- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority {

    CGSize size = [super systemLayoutSizeFittingSize:targetSize withHorizontalFittingPriority:horizontalFittingPriority verticalFittingPriority:verticalFittingPriority];

    // 这个不能少,否则 self.coll.collectionViewLayout.collectionViewContentSize.height = 0
    [self.coll layoutIfNeeded];

    // 这里得到 UICollectionView 内容的宽高
    CGSize collSize = self.coll.collectionViewLayout.collectionViewContentSize;

    return CGSizeMake(size.width, size.height+collSize.height);
}

注意

使用 Autolayout 布局 UITableViewCell 并实现动态高度时,一定要确保约束能够撑开 UITableViewCell 否则将会得到错误的高度44。