如何在表视图中的UITableViewCells中使用“自动布局”,让每个单元格的内容和子视图确定行高度(自身/自动),同时保持平滑的滚动性能?
当前回答
UITableView.automaticDimension可以通过接口生成器设置:
Xcode>情节提要>尺寸检查器
表格视图单元格>行高度>自动
其他回答
以防人们仍然对此感到困扰。我写了一篇关于在UITableViews中使用Autolayout的快速博客文章,利用Autolayout For Dynamic Cell Heights以及一个开源组件来帮助实现这一点,使其更加抽象和易于实现。https://github.com/Raizlabs/RZCellSizeManager
我将@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中演奏爵士乐,但现在就可以了!
我只是对rowHeight和estimatedRowHeight的两个值进行了一些愚蠢的尝试和错误,并认为它可能会提供一些调试细节:
如果同时设置它们,或者仅设置估计的RowHeight,则将获得所需的行为:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 1.00001 // MUST be greater than 1
建议您尽最大努力得到正确的估计,但最终结果并无不同。这只会影响你的表现。
如果只设置rowHeight,即只执行以下操作:
tableView.rowHeight = UITableViewAutomaticDimension
您的最终结果不会如预期:
如果将estimatedRowHeight设置为1或更小,则无论rowHeight如何,都会崩溃。
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 1
我崩溃了,出现以下错误消息:
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'table view row height
must not be negative - provided height for index path (<NSIndexPath:
0xc000000000000016> {length = 2, path = 0 - 0}) is -1.000000'
...some other lines...
libc++abi.dylib: terminating with uncaught exception of type
NSException
我刚刚遇到一个非常重要的问题,作为回答发布。
@smileyborg的回答基本正确。但是,如果自定义单元格类的layoutSubviews方法中有任何代码,例如设置preferredMaxLayoutWidth,则不会使用以下代码运行:
[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];
这让我困惑了一阵子。然后我意识到这是因为它们只触发contentView上的layoutSubview,而不是单元格本身。
我的工作代码如下:
TCAnswerDetailAppSummaryCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TCAnswerDetailAppSummaryCell"];
[cell configureWithThirdPartyObject:self.app];
[cell layoutIfNeeded];
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height;
注意,如果您正在创建一个新的单元格,我很确定您不需要调用setNeedsLayout,因为它应该已经设置好了。在保存对单元格的引用的情况下,您可能应该调用它。不管怎样,它都不会造成任何影响。
另一个提示是,如果您使用的是单元格子类,则需要设置preferredMaxLayoutWidth等属性。正如@smileyborg提到的,“您的表视图单元格的宽度尚未固定到表视图的宽度”。这是正确的,如果您在子类中而不是在视图控制器中进行工作,则会遇到麻烦。但是,此时可以使用表格宽度简单地设置单元格框架:
例如,在计算高度时:
self.summaryCell = [self.tableView dequeueReusableCellWithIdentifier:@"TCAnswerDetailDefaultSummaryCell"];
CGRect oldFrame = self.summaryCell.frame;
self.summaryCell.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, self.tableView.frame.size.width, oldFrame.size.height);
(我碰巧缓存了这个特定的单元以供重用,但这无关紧要)。
要设置行高和估计行高的自动尺寸,请确保以下步骤对单元格/行高布局有效。
分配和实现表视图数据源和委托将UITableViewAutomaticDimension分配给rowHeight和estimatedRowHeight实现委托/dataSource方法(即heightForRowAt并向其返回值UITableViewAutomaticDimension)
-
目标C:
// in ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
@property IBOutlet UITableView * table;
@end
// in ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.table.dataSource = self;
self.table.delegate = self;
self.table.rowHeight = UITableViewAutomaticDimension;
self.table.estimatedRowHeight = UITableViewAutomaticDimension;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
斯威夫特:
@IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Don't forget to set dataSource and delegate for table
table.dataSource = self
table.delegate = self
// Set automatic dimensions for row height
// Swift 4.2 onwards
table.rowHeight = UITableView.automaticDimension
table.estimatedRowHeight = UITableView.automaticDimension
// Swift 4.1 and below
table.rowHeight = UITableViewAutomaticDimension
table.estimatedRowHeight = UITableViewAutomaticDimension
}
// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// Swift 4.2 onwards
return UITableView.automaticDimension
// Swift 4.1 and below
return UITableViewAutomaticDimension
}
对于UITableviewCell中的标签实例
设置行数=0(换行模式=截断尾部(&L)设置与其超级视图/单元格容器相关的所有约束(顶部、底部、左右)。可选:如果您希望标签覆盖最小垂直区域,即使没有数据,也可以设置标签的最小高度。
注意:如果您有多个标签(UIElements)具有动态长度,应根据其内容大小进行调整:请调整要以更高优先级展开/压缩的标签的“内容拥抱和抗压优先级”。
推荐文章
- 如何删除默认的导航栏空间在SwiftUI导航视图
- 如何在iOS中使用Swift编程segue
- Swift -整数转换为小时/分钟/秒
- Swift:声明一个空字典
- 在成功提交我的应用程序后,“太多符号文件”
- 首先添加一个UIView,甚至是导航栏
- 我如何改变UIButton标题颜色?
- 在Swift中如何调用GCD主线程上的参数方法?
- NSLayoutConstraints是可动画的吗?
- iOS -构建失败,CocoaPods无法找到头文件
- CFNetwork SSLHandshake iOS 9失败
- 请求失败:不可接受的内容类型:文本/html使用AFNetworking 2.0
- 缺少推荐的图标文件-该包不包含iPhone / iPod Touch的应用程序图标,像素为“120x120”,png格式
- 以编程方式创建segue
- 如何使用Xcode创建。ipa文件?