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 ...
随机推荐
- C# Socket传输大文件
1.基础类TransferFiles,client和server都需要 using System; using System.Collections.Generic; using System.Tex ...
- 查看Linux相关信息
1."uname -a ",可显示电脑以及操作系统的相关信息. 2."cat /proc/version",说明正在运行的内核版本. 3."cat / ...
- 华为oj 统计字符串不同字符
#include <stdio.h> #include <string.h> int firstSingle(char *str) { int hash[128]={0}; f ...
- php连接mysql的一些方法总结
为了能让PHP连接MSSQL,系统需要安装MSSQL,PHP,且在PHP.ini中的配置中,将 ;extension=php_mssql.dll前面的;去掉 1.连接MSSQL ...
- Java protobuf框架使用向导
ProtoBuf,全称是Protocol Buffers, 它是谷歌内部用的一种高效的.可扩展的对结构化数据进行编码的格式规范.谷歌自己内部很多程序之间的通信协议都用了ProtoBuf. 下面介绍的是 ...
- mysql分表方法实现
一般来说,当我们的数据库的数据超过了100w记录的时候就应该考虑分表或者分区了,这次我来详细说说分表的一些方法.目前我所知道的方法都是MYISAM的,INNODB如何做分表并且保留事务和外键,我还不是 ...
- MongoDB资料--Java驱动, Hadoop驱动, Spark使用
MongoDB数据库备份: mongodump -h 192.168.1.160 -d MapLoc -o /usr/local/myjar/mongo/MapLoc/数据库还原:mongoresto ...
- TestNG使用Eclipse建立Test Case - 就是爱Java
除了JUnit可以进行单元测试外,还可以使用TestNG来撰写Test Case,这是另一种测试Framework,它是为更广泛的测试场合而设计,可以运行在没有修改过的JUnit测试,除非看到它们的i ...
- 一直想测试的NGINX变量输出,最于有办法了。
参考URL: http://blog.ailms.me/2013/08/04/nginx-server_name-and-host-difference.html 要是可能在正则测试及REWRITE就 ...
- 如何给开源的DUILib支持Accessibility(论述了DUILib的六个缺点,很精彩)
最近的工作是给开源的DUILib支持Accessibility, 一些经验记录并分享下. 微软的Accessibility其实Windows平台上一个挺重要的东西, 尽管在国内不受重视,但是如果你 ...