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 ...
随机推荐
- $(document).ready(); $().ready(); $()
$(document).ready(function(){}); $().ready(function(){}); $(function(){}), 三者效果是一样的,在文档加载完成之后执行()中的代 ...
- 在C#、Java中,为什么不能用[返回值]区别重载方法?
为什么方法签名只包含方法名和参数列表,而没有把返回值考虑进去? 如下有两个方法: void Func(){} string Func() { return string.Empty; } 编辑器可以根 ...
- Asp.Net WebAPI Get提交、Post提交处理
1.启用跨域提交 <system.webServer> <httpProtocol> <customHeaders> <add name="Acce ...
- oralce 恢复Delete删除
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual; select * from db_datatable as of timestam ...
- myeclipse笔记(3):导入的项目切换jdk版本
有时候,从外面导入的javaweb项目会访问不了,这个时候改变jdk版本就是其中解决的方法之一. 右键点击项目 --> bulid path --> configure 选择需要 ...
- Crontab 计划任务
有一部分工作,需要某个时间开始,每天执行,每间断一段时间执行.这个时候就需要了crontab.crontab 管理着linux上一些定期的任务.log rotate,logwatch 等等废话不多说来 ...
- test在博客中嵌入实例代码
// 其实很简单,只要把css.div.js代码直接写在里面就可以了.通过查看源代码就能看得很清楚了. 要注意的一点就是:如果div里没有任何内容,则它会被(博客网)删除掉,所以即使没有内容, ...
- [Mugeda HTML5技术教程之3] Hello World: 第一个Mugeda动画
今天我们开始我们的第一个Mugeda动画作品,并通过它来看看制作Mugeda动画的一些通用流程.在开始制作之前,请确保你已经拥有一个Mugeda网站的账号.如果还没有,你可以登录 www.mugeda ...
- jQuery学习资源参考教程网址推荐
jQuery官方主页:http://jquery.comjQuery中文入门指南:http://www.k99k.com/jQuery_getting_started.htmljQuery使用手册:h ...
- AFNetworking 2.0 新特性讲解之AFHTTPSessionManager
AFNetworking 2.0 新特性讲解之AFHTTPSessionManager (2014-02-17 11:56:24) 转载▼ AFNetworking 2.0 相比1.0 API ...