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 ...
随机推荐
- 学习okhttp wiki--HTTPS
HTTPS OkHttp尝试平衡两个相互竞争的要素: 连通性(Connectivity):连接到尽可能多的服务器.这包括运行最新版本 boringssl 的服务器和不太过时的老版本 OpenSSL 的 ...
- tomcat下出现The file is absent or does not have execute&
启动tomcat出现The file is absent or does not have execute permission... Cannot find bin/catalina.sh The ...
- web前端工程师学习之路开启(前言)
web前端工程师需要掌握的所有技能 图解1: 图解2:
- JavaWeb学习笔记之Servlet(一)
1. 引子: 当我们开始进入JavaWeb开发的学习时,我们就必须要和Servlet和HTTP这两个词进行打交道了,尤其是Servlet.即使到了后面使用JSP (我们知道JSP其本身就是一个Serv ...
- poj3468 线段树+lazy标记
A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 92921 ...
- ON UPDATE CURRENT_TIMESTAMP
CREATE TABLE time1 ( id SMALLINT, time1 TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TI ...
- Java系列--第四篇 基于Maven的SSME之发送邮件
在系列第一篇中,使用的是mybatis得到了一个小小的项目,而该项目的用户对象是有邮件地址的,如果按照邮件地址给对方去一封邮件会不会更能体现针对性呢,所以,我在这篇准备加入发送邮件的功能,利用的就是s ...
- php 之 类,对象
--恢复内容结束--- 一.类和对象: 1.定义: 对象:我们所见到的东西都可以称之为对象,是类实例化出来的东西 类:是对所有的同类对象抽象出来的东西 eg: 在一张表中记录了全班同学的学号,姓名,性 ...
- [Flask Security]当不能通过认证的时候制定跳转
Flask Security这个插件能对用户权限进行很好的控制. 通过三个model实现: User,存放用户数据 Role,存放角色数据 User_Role.存放用户角色信息 user_datast ...
- Spring ioc 原理
java程序员都知道:java程序中的每个业务逻辑至少需要两个或以上的对象来协作完成,通常,每个对象在使用他的合作对象时,自己均要使用像new object() 这样的语法来完成合作对象的申请工作.你 ...