如何在表视图中的UITableViewCells中使用“自动布局”,让每个单元格的内容和子视图确定行高度(自身/自动),同时保持平滑的滚动性能?


当前回答

像@Bob Spryn一样,我遇到了一个非常重要的问题,我把这个问题作为答案发布。

我纠结于@smileyborg的回答了一会儿。我遇到的问题是,如果您在IB中使用其他元素(UILabels、UIButtons等)定义了原型单元,当您使用[[YourTableViewCellClass alloc]init]实例化该单元时,它不会实例化该单元中的所有其他元素,除非您编写了这样做的代码。(我对initWithStyle有过类似的经历。)

要让故事板实例化所有其他元素,请使用[tableView dequeueReusableCellWithIdentifier:@“DoseNeed”](不是[tableView de queueReusable CellWithIdentifier:forIndexPath:],因为这会引起有趣的问题。)执行此操作时,将实例化IB中定义的所有元素。

其他回答

像@Bob Spryn一样,我遇到了一个非常重要的问题,我把这个问题作为答案发布。

我纠结于@smileyborg的回答了一会儿。我遇到的问题是,如果您在IB中使用其他元素(UILabels、UIButtons等)定义了原型单元,当您使用[[YourTableViewCellClass alloc]init]实例化该单元时,它不会实例化该单元中的所有其他元素,除非您编写了这样做的代码。(我对initWithStyle有过类似的经历。)

要让故事板实例化所有其他元素,请使用[tableView dequeueReusableCellWithIdentifier:@“DoseNeed”](不是[tableView de queueReusable CellWithIdentifier:forIndexPath:],因为这会引起有趣的问题。)执行此操作时,将实例化IB中定义的所有元素。

在我的例子中,我必须创建一个自定义单元格,其中包含来自服务器的图像,可以是任何宽度和高度。以及两个具有动态大小(宽度和高度)的UILabels

我在回答自动布局和编程实现了同样的效果:

基本上在@smileyBorg以上的回答有所帮助,但systemLayoutSizeFittingSize从未对我奏效,在我的方法中:

1.不使用自动行高计算属性。2.不使用估计高度3.不需要不必要的更新约束。4.不使用自动首选最大布局宽度。5.不使用systemLayoutSizeFittingSize(应该使用但不适用于我,我不知道它在内部做什么),而是使用我的方法-(float)getViewHeight,我知道它在在内部做些什么。

当我使用几种不同的方式显示单元格时,UITableView单元格中的高度是否可能不同?

假设您有一个带有子视图的单元格,并且希望该单元格的高度足够高,以包含子视图+填充。

1) 将子视图的底部约束设置为cell.contentView减去所需的填充。不要对单元格或cell.contentView本身设置约束。

2) 将tableView的rowHeight属性或tableView:heightForRowAtIndexPath:设置为UITableViewAutomaticDimension。

3) 将tableView的estimatedRowHeight属性或tableView:estimatedHeightForRowAtIndexPath:设置为高度的最佳猜测。

就是这样。

我必须使用动态视图(通过代码设置视图和约束),当我想设置preferredMaxLayoutWidth标签的宽度为0时。所以我有错误的细胞高度。

然后我添加了

[cell layoutSubviews];

执行之前

[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];

之后,标签的宽度符合预期,动态高度计算正确。

我将@smileyborg的iOS7解决方案归入一个类别

我决定用@smileyborg将这个聪明的解决方案包装成UICollectionViewCell+AutoLayoutDynamicHeightCalculation类别。

该类别还纠正了@wildsmonkey的回答中概述的问题(从笔尖和systemLayoutSizeFittingSize加载单元格:返回CGRectZero)

它不考虑任何缓存,但现在适合我的需要。随意复制、粘贴和破解。

UI集合视图单元格+自动布局动态高度计算.h

#import <UIKit/UIKit.h>

typedef void (^UICollectionViewCellAutoLayoutRenderBlock)(void);

/**
 *  A category on UICollectionViewCell to aid calculating dynamic heights based on AutoLayout contraints.
 *
 *  Many thanks to @smileyborg and @wildmonkey
 *
 *  @see stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights
 */
@interface UICollectionViewCell (AutoLayoutDynamicHeightCalculation)

/**
 *  Grab an instance of the receiving type to use in order to calculate AutoLayout contraint driven dynamic height. The method pulls the cell from a nib file and moves any Interface Builder defined contrainsts to the content view.
 *
 *  @param name Name of the nib file.
 *
 *  @return collection view cell for using to calculate content based height
 */
+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name;

/**
 *  Returns the height of the receiver after rendering with your model data and applying an AutoLayout pass
 *
 *  @param block Render the model data to your UI elements in this block
 *
 *  @return Calculated constraint derived height
 */
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width;

/**
 *  Directly calls `heightAfterAutoLayoutPassAndRenderingWithBlock:collectionViewWidth` assuming a collection view width spanning the [UIScreen mainScreen] bounds
 */
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block;

@end

UI集合视图单元格+自动布局动态高度计算.m

#import "UICollectionViewCell+AutoLayout.h"

@implementation UICollectionViewCell (AutoLayout)

#pragma mark Dummy Cell Generator

+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name
{
    UICollectionViewCell *heightCalculationCell = [[[NSBundle mainBundle] loadNibNamed:name owner:self options:nil] lastObject];
    [heightCalculationCell moveInterfaceBuilderLayoutConstraintsToContentView];
    return heightCalculationCell;
}

#pragma mark Moving Constraints

- (void)moveInterfaceBuilderLayoutConstraintsToContentView
{
    [self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
        [self removeConstraint:constraint];
        id firstItem = constraint.firstItem == self ? self.contentView : constraint.firstItem;
        id secondItem = constraint.secondItem == self ? self.contentView : constraint.secondItem;
        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:firstItem
                                                                     attribute:constraint.firstAttribute
                                                                     relatedBy:constraint.relation
                                                                        toItem:secondItem
                                                                     attribute:constraint.secondAttribute
                                                                    multiplier:constraint.multiplier
                                                                      constant:constraint.constant]];
    }];
}

#pragma mark Height

- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block
{
    return [self heightAfterAutoLayoutPassAndRenderingWithBlock:block
                                            collectionViewWidth:CGRectGetWidth([[UIScreen mainScreen] bounds])];
}

- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width
{
    NSParameterAssert(block);

    block();

    [self setNeedsUpdateConstraints];
    [self updateConstraintsIfNeeded];

    self.bounds = CGRectMake(0.0f, 0.0f, width, CGRectGetHeight(self.bounds));

    [self setNeedsLayout];
    [self layoutIfNeeded];

    CGSize calculatedSize = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];

    return calculatedSize.height;

}

@end

用法示例:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MYSweetCell *cell = [MYSweetCell heightCalculationCellFromNibWithName:NSStringFromClass([MYSweetCell class])];
    CGFloat height = [cell heightAfterAutoLayoutPassAndRenderingWithBlock:^{
        [(id<MYSweetCellRenderProtocol>)cell renderWithModel:someModel];
    }];
    return CGSizeMake(CGRectGetWidth(self.collectionView.bounds), height);
}

谢天谢地,我们不必在iOS8中演奏爵士乐,但现在就可以了!