Fantageek翻译系列之《使用Autolayout显示变化高度的UITableViewCell》
这篇博客主要在于,解释如何通过仅仅使用Autolayout很很少的代码,显示高度不同的Cell。虽然标题说的是TableView,但是CollectionView同样适合。但是,这种方法只使用iOS7和iOS8。
在Github上的实例代码是DynamicTableViewCellHeight。
这个Demo显示了一些名人名言,他看起来像这样:

preferredMaxLayoutWidth
这种方法,主要来自于preferredMaxLayoutWidth属性。对于更多高级用法,请看Auto Layout and Views that Wrap。
User Interface
我使用的是Storyboard, 但是你能够使用xib或者代码, 对于如何使用代码,你可以看一下这篇文章AutoSize UITableViewCell height programmatically。


这里,“引言Label”显示多行(通过设置numberOfLines属性为0)。因为我们有2个Label,AutoLayout不知道如何扩大,不知道cell的大小改变时哪一个Label保持大小不变。在这种情况下,我想要“引言Label”扩大,所以减少减少垂直方向Hugging优先级,并且增加保持自身大小不变优先级。
关于hugging和resistance优先级的区别,可以看这篇文章Cocoa Autolayout: content hugging vs content compression resistance priority。
注意:
1、Dynamic Table View Cell Height and Auto Layout,这篇博客告诉我们,我们应该给labels的“intrinsic content”属性到1000,并且设置Intrinsic Size为占位符大小。我认为这是不需要的。
2、你必须给TableViewCell的contentView设置属性。
3、对于在Interface Builder中的CollectionViewCell。你不会看到contentView,但是你真的是和contentView打交道。
Cell
QuoteTableViewCell.h
@interface QuoteTableViewCell : UITableViewCell
@property (weak, nonatomic) IBOutlet UILabel *numberLabel;
@property (weak, nonatomic) IBOutlet UILabel *quoteLabel; @end
QuoteTableViewCell.m
@implementation QuoteTableViewCell // (1)
- (void)setBounds:(CGRect)bounds
{
[super setBounds:bounds]; self.contentView.frame = self.bounds;
} - (void)layoutSubviews
{
[super layoutSubviews]; // (2)
[self.contentView updateConstraintsIfNeeded];
[self.contentView layoutIfNeeded]; // (3)
self.quoteLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.quoteLabel.frame);
} @end
ViewController
ViewController.m
#define SYSTEM_VERSION ([[UIDevice currentDevice] systemVersion])
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([SYSTEM_VERSION compare:v options:NSNumericSearch] != NSOrderedAscending)
#define IS_IOS8_OR_ABOVE (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) @interface ViewController () <UITableViewDataSource, UITableViewDelegate> @property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic, strong) NSArray *items;
@property (nonatomic, strong) QuoteTableViewCell *prototypeCell; @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; [self setupTableView];
[self loadData];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} #pragma mark - Setup
- (void)setupTableView
{
self.tableView.dataSource = self;
self.tableView.delegate = self;
} #pragma mark - Data
- (void)loadData
{
NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"quotes" ofType:@"plist"];
self.items = [[NSArray alloc] initWithContentsOfFile:plistPath]; [self.tableView reloadData];
} #pragma mark - PrototypeCell
// (4)
- (QuoteTableViewCell *)prototypeCell
{
if (!_prototypeCell) {
_prototypeCell = [self.tableView dequeueReusableCellWithIdentifier:NSStringFromClass([QuoteTableViewCell class])];
} return _prototypeCell;
} #pragma mark - Configure
- (void)configureCell:(QuoteTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *quote = self.items[indexPath.row]; cell.numberLabel.text = [NSString stringWithFormat:@"Quote %ld", (long)indexPath.row];
cell.quoteLabel.text = quote;
} #pragma mark - UITableViewDataSouce
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return ;
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.items.count;
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
QuoteTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([QuoteTableViewCell class])]; [self configureCell:cell forRowAtIndexPath:indexPath]; return cell;
} #pragma mark - UITableViewDelegate
// (5)
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
} - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// (6)
if (IS_IOS8_OR_ABOVE) {
return UITableViewAutomaticDimension;
} // (7)
//self.prototypeCell.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(self.prototypeCell.bounds)); [self configureCell:self.prototypeCell forRowAtIndexPath:indexPath]; // (8)
[self.prototypeCell updateConstraintsIfNeeded];
[self.prototypeCell layoutIfNeeded]; // (9)
return [self.prototypeCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; } @end
注意以下几点:
1、Auto Layout in UICollectionViewCell not working
2、AutoSize UITableViewCell height programmatically
Make sure the contentView does a layout pass here so that its subviews have their frames set, which we need to use to set the preferredMaxLayoutWidth below. Set the preferredMaxLayoutWidth of the mutli-line bodyLabel based on the evaluated width of the label’s frame, as this will allow the text to wrap correctly, and as a result allow the label to take on the correct height.
3、你只需要调用[self.contentView layoutIfNeeded]。
4、如果你要改变某些限制,你需要调用[self.contentView updateConstraintsIfNeeded]。
5、如果你调用[self.contentView updateConstraintsIfNeeded],你必须在之前调用[self.contentView layoutIfNeeded]。
6、不需要调用[self.contentView setsNeedLayout],或者self.contentView setsNeedUpdateConstraints]。
ViewController
#define SYSTEM_VERSION ([[UIDevice currentDevice] systemVersion])
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([SYSTEM_VERSION compare:v options:NSNumericSearch] != NSOrderedAscending)
#define IS_IOS8_OR_ABOVE (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) @interface ViewController () <UITableViewDataSource, UITableViewDelegate> @property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic, strong) NSArray *items;
@property (nonatomic, strong) QuoteTableViewCell *prototypeCell; @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; [self setupTableView];
[self loadData];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} #pragma mark - Setup
- (void)setupTableView
{
self.tableView.dataSource = self;
self.tableView.delegate = self;
} #pragma mark - Data
- (void)loadData
{
NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"quotes" ofType:@"plist"];
self.items = [[NSArray alloc] initWithContentsOfFile:plistPath]; [self.tableView reloadData];
} #pragma mark - PrototypeCell
// (4)
- (QuoteTableViewCell *)prototypeCell
{
if (!_prototypeCell) {
_prototypeCell = [self.tableView dequeueReusableCellWithIdentifier:NSStringFromClass([QuoteTableViewCell class])];
} return _prototypeCell;
} #pragma mark - Configure
- (void)configureCell:(QuoteTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *quote = self.items[indexPath.row]; cell.numberLabel.text = [NSString stringWithFormat:@"Quote %ld", (long)indexPath.row];
cell.quoteLabel.text = quote;
} #pragma mark - UITableViewDataSouce
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return ;
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.items.count;
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
QuoteTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([QuoteTableViewCell class])]; [self configureCell:cell forRowAtIndexPath:indexPath]; return cell;
} #pragma mark - UITableViewDelegate
// (5)
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
} - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// (6)
if (IS_IOS8_OR_ABOVE) {
return UITableViewAutomaticDimension;
} // (7)
//self.prototypeCell.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(self.prototypeCell.bounds)); [self configureCell:self.prototypeCell forRowAtIndexPath:indexPath]; // (8)
[self.prototypeCell updateConstraintsIfNeeded];
[self.prototypeCell layoutIfNeeded]; // (9)
return [self.prototypeCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; } @end
7、原型cell绝不会显示出来,它用来布局一个cell并且决定一个需要的高度。
8、你能够使用UITableViewAutomaticDimension,或者使用一个大约合理的高度。
9、iOS8 autoSizing属性需要使用UITableViewAutomaticDimension。
Fantageek翻译系列之《使用Autolayout显示变化高度的UITableViewCell》的更多相关文章
- 《Entity Framework 6 Recipes》中文翻译系列 目录篇 -持续更新
为了方便大家的阅读和学习,也是响应网友的建议,在这里为这个系列做一个目录.在目录开始这前,我先来回答之前遇到的几个问题. 1.为什么要学习EF? 这个问题很简单,项目需要.这不像学校,没人强迫你学习! ...
- 使用Material Design 创建App翻译系列----材料主题的使用(Using Material Theme)
上一篇是使用Material Design 创建App翻译系列--開始学习篇,进入正题: 新的材料主题提供了下面内容: 1. 提供了同意设置颜色板的系统部件组件. 2. 为这些系统组件提供了触摸反馈动 ...
- ASP.NET MVC with Entity Framework and CSS一书翻译系列文章之第六章:管理产品图片——多对多关系(上篇)
在这章中,我们将学习如何创建一个管理图片的新实体,如何使用HTML表单上传图片文件,并使用多对多关系将它们和产品关联起来,如何将图片存储在文件系统中.在这章中,我们还会学习更加复杂的异常处理,如何向模 ...
- 21.翻译系列:Entity Framework 6 Power Tools【EF 6 Code-First系列】
原文链接:https://www.entityframeworktutorial.net/code-first/entity-framework-power-tools.aspx 大家好,这里就是EF ...
- 《Entity Framework 6 Recipes》中文翻译 ---- 系列教程
为了方便大家的阅读和学习,也是响应网友的建议,在这里为这个系列做一个目录.在目录开始这前,我先来回答之前遇到的几个问题. 1.为什么要学习EF? 这个问题很简单,项目需要.这不像学校,没人强迫你学习! ...
- 使用Material Design 创建App翻译系列---列表和卡片集的创建
上一篇是使用Material Design 创建App翻译系列--材料主题的使用(Using Material Theme),进入正题: 想要在应用里创建Material Design风格的复杂列表和 ...
- 20.2.翻译系列:EF 6中基于代码的数据库迁移技术【EF 6 Code-First系列】
原文链接:https://www.entityframeworktutorial.net/code-first/code-based-migration-in-code-first.aspx EF 6 ...
- 20.1翻译系列:EF 6中自动数据迁移技术【EF 6 Code-First系列】
原文链接:https://www.entityframeworktutorial.net/code-first/automated-migration-in-code-first.aspx EF 6 ...
- 20.翻译系列:Code-First中的数据库迁移技术【EF 6 Code-First系列】
原文链接:https://www.entityframeworktutorial.net/code-first/migration-in-code-first.aspx EF 6 Code-First ...
随机推荐
- Resizable(调整大小)组件
一.加载方式 //class 加载方式 <div id="rr" class="easyui-resizable" data-options=" ...
- (转)c#缓存介绍
在 ASP.NET 提供的许多特性中,缓存支持无疑是最值得欣赏的特性.相比 ASP.NET 的所有其他特性,缓存对应用程序的性能具有最大的潜在影响,利用缓存和其他机制,ASP.NET 开发人员可以接受 ...
- Fiddler使用笔记
http://www.cnblogs.com/TankXiao/archive/2012/02/06/2337728.html#basic 1.filter的使用,跟踪某个网站的访问,例如:hr. ...
- 重新认识Intent
相信android开发工程师,对Intent一定不陌生,在整个开发中随时都用到了,今天我们总结一下Intent. 1. 为什么需要Intent? 在android Intent机制是协助应用间的交互与 ...
- PM2.5空气质量指数(AQI)是如何计算的
AQI如何计算 具体要计算PM2.5空气质量指数(AQI),SENBE申贝技术人员向您介绍如何计算的公式. 比如当实测浓度小于等于0.035时,根据实测浓度×50/0.035计算.例如 ...
- 一个简单C程序的汇编代码分析
几个重要的寄存器 eip - 用于存放当前所执行的指令地址 esp - 栈(顶)指针寄存器 ebp - 基址(栈底)指针寄存器 简单的C程序 int g(int x) { ; } int f(int ...
- JavaScript学习笔记(三十八) 复制属性继承
复制属性继承(Inheritance by Copying Properties) 让我们看一下另一个继承模式—复制属性继承(inheritance by copying properties).在这 ...
- 兼容IE的渐变
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=, startColorstr=#1471da, endColorstr ...
- Effective Java实作Comparator - 就是爱Java
如果集合或数组内的对象,有1个以上不同的排序逻辑时,那该如何处理呢?尤其是当已经实现了Comparable,又不能变动原本的逻辑时,Mix会采用Comparator来处理. 阅读全文>>
- 自制单片机之十二……AT89C2051烧写器的制做与调试
现在都用S52了,还用C2051干嘛!价格也差不多.但是C2051的体积要比S51.S52小很多,而且引脚只有20只,在一些简单的控制中,这些引脚已足够了,小的体积更具有优势些.但目前好像还没有支持在 ...