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

1 @interface QuoteTableViewCell : UITableViewCell
2 @property (weak, nonatomic) IBOutlet UILabel *numberLabel;
3 @property (weak, nonatomic) IBOutlet UILabel *quoteLabel;
4
5 @end

QuoteTableViewCell.m

 1 @implementation QuoteTableViewCell
 2
 3 // (1)
 4 - (void)setBounds:(CGRect)bounds
 5 {
 6     [super setBounds:bounds];
 7
 8     self.contentView.frame = self.bounds;
 9 }
10
11 - (void)layoutSubviews
12 {
13     [super layoutSubviews];
14
15     // (2)
16     [self.contentView updateConstraintsIfNeeded];
17     [self.contentView layoutIfNeeded];
18
19     // (3)
20     self.quoteLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.quoteLabel.frame);
21 }
22
23 @end

ViewController

ViewController.m

  1 #define SYSTEM_VERSION                              ([[UIDevice currentDevice] systemVersion])
  2 #define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([SYSTEM_VERSION compare:v options:NSNumericSearch] != NSOrderedAscending)
  3 #define IS_IOS8_OR_ABOVE                            (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0"))
  4
  5 @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
  6
  7 @property (weak, nonatomic) IBOutlet UITableView *tableView;
  8 @property (nonatomic, strong) NSArray *items;
  9 @property (nonatomic, strong) QuoteTableViewCell *prototypeCell;
 10
 11 @end
 12
 13 @implementation ViewController
 14
 15 - (void)viewDidLoad {
 16     [super viewDidLoad];
 17
 18     [self setupTableView];
 19     [self loadData];
 20 }
 21
 22 - (void)didReceiveMemoryWarning {
 23     [super didReceiveMemoryWarning];
 24     // Dispose of any resources that can be recreated.
 25 }
 26
 27 #pragma mark - Setup
 28 - (void)setupTableView
 29 {
 30     self.tableView.dataSource = self;
 31     self.tableView.delegate = self;
 32 }
 33
 34 #pragma mark - Data
 35 - (void)loadData
 36 {
 37     NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"quotes" ofType:@"plist"];
 38     self.items = [[NSArray alloc] initWithContentsOfFile:plistPath];
 39
 40     [self.tableView reloadData];
 41 }
 42
 43 #pragma mark - PrototypeCell
 44 // (4)
 45 - (QuoteTableViewCell *)prototypeCell
 46 {
 47     if (!_prototypeCell) {
 48         _prototypeCell = [self.tableView dequeueReusableCellWithIdentifier:NSStringFromClass([QuoteTableViewCell class])];
 49     }
 50
 51     return _prototypeCell;
 52 }
 53
 54 #pragma mark - Configure
 55 - (void)configureCell:(QuoteTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
 56 {
 57     NSString *quote = self.items[indexPath.row];
 58
 59     cell.numberLabel.text = [NSString stringWithFormat:@"Quote %ld", (long)indexPath.row];
 60     cell.quoteLabel.text = quote;
 61 }
 62
 63 #pragma mark - UITableViewDataSouce
 64 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
 65 {
 66     return 1;
 67 }
 68
 69 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
 70 {
 71     return self.items.count;
 72 }
 73
 74 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
 75 {
 76     QuoteTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([QuoteTableViewCell class])];
 77
 78     [self configureCell:cell forRowAtIndexPath:indexPath];
 79
 80     return cell;
 81 }
 82
 83 #pragma mark - UITableViewDelegate
 84 // (5)
 85 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
 86 {
 87     return UITableViewAutomaticDimension;
 88 }
 89
 90 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
 91 {
 92     // (6)
 93     if (IS_IOS8_OR_ABOVE) {
 94         return UITableViewAutomaticDimension;
 95     }
 96
 97     // (7)
 98     //self.prototypeCell.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(self.prototypeCell.bounds));
 99
100     [self configureCell:self.prototypeCell forRowAtIndexPath:indexPath];
101
102     // (8)
103     [self.prototypeCell updateConstraintsIfNeeded];
104     [self.prototypeCell layoutIfNeeded];
105
106     // (9)
107     return [self.prototypeCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
108
109 }
110
111 @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

  1 #define SYSTEM_VERSION                              ([[UIDevice currentDevice] systemVersion])
  2 #define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([SYSTEM_VERSION compare:v options:NSNumericSearch] != NSOrderedAscending)
  3 #define IS_IOS8_OR_ABOVE                            (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0"))
  4
  5 @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
  6
  7 @property (weak, nonatomic) IBOutlet UITableView *tableView;
  8 @property (nonatomic, strong) NSArray *items;
  9 @property (nonatomic, strong) QuoteTableViewCell *prototypeCell;
 10
 11 @end
 12
 13 @implementation ViewController
 14
 15 - (void)viewDidLoad {
 16     [super viewDidLoad];
 17
 18     [self setupTableView];
 19     [self loadData];
 20 }
 21
 22 - (void)didReceiveMemoryWarning {
 23     [super didReceiveMemoryWarning];
 24     // Dispose of any resources that can be recreated.
 25 }
 26
 27 #pragma mark - Setup
 28 - (void)setupTableView
 29 {
 30     self.tableView.dataSource = self;
 31     self.tableView.delegate = self;
 32 }
 33
 34 #pragma mark - Data
 35 - (void)loadData
 36 {
 37     NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"quotes" ofType:@"plist"];
 38     self.items = [[NSArray alloc] initWithContentsOfFile:plistPath];
 39
 40     [self.tableView reloadData];
 41 }
 42
 43 #pragma mark - PrototypeCell
 44 // (4)
 45 - (QuoteTableViewCell *)prototypeCell
 46 {
 47     if (!_prototypeCell) {
 48         _prototypeCell = [self.tableView dequeueReusableCellWithIdentifier:NSStringFromClass([QuoteTableViewCell class])];
 49     }
 50
 51     return _prototypeCell;
 52 }
 53
 54 #pragma mark - Configure
 55 - (void)configureCell:(QuoteTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
 56 {
 57     NSString *quote = self.items[indexPath.row];
 58
 59     cell.numberLabel.text = [NSString stringWithFormat:@"Quote %ld", (long)indexPath.row];
 60     cell.quoteLabel.text = quote;
 61 }
 62
 63 #pragma mark - UITableViewDataSouce
 64 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
 65 {
 66     return 1;
 67 }
 68
 69 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
 70 {
 71     return self.items.count;
 72 }
 73
 74 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
 75 {
 76     QuoteTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([QuoteTableViewCell class])];
 77
 78     [self configureCell:cell forRowAtIndexPath:indexPath];
 79
 80     return cell;
 81 }
 82
 83 #pragma mark - UITableViewDelegate
 84 // (5)
 85 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
 86 {
 87     return UITableViewAutomaticDimension;
 88 }
 89
 90 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
 91 {
 92     // (6)
 93     if (IS_IOS8_OR_ABOVE) {
 94         return UITableViewAutomaticDimension;
 95     }
 96
 97     // (7)
 98     //self.prototypeCell.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(self.prototypeCell.bounds));
 99
100     [self configureCell:self.prototypeCell forRowAtIndexPath:indexPath];
101
102     // (8)
103     [self.prototypeCell updateConstraintsIfNeeded];
104     [self.prototypeCell layoutIfNeeded];
105
106     // (9)
107     return [self.prototypeCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
108
109 }
110
111 @end

7、原型cell绝不会显示出来,它用来布局一个cell并且决定一个需要的高度。

8、你能够使用UITableViewAutomaticDimension,或者使用一个大约合理的高度。

9、iOS8 autoSizing属性需要使用UITableViewAutomaticDimension。

时间: 2024-10-29 19:09:54

Fantageek翻译系列之《使用Autolayout显示变化高度的UITableViewCell》的相关文章

使用Material Design 创建App翻译系列----材料主题的使用(Using Material Theme)

上一篇是使用Material Design 创建App翻译系列--開始学习篇,进入正题: 新的材料主题提供了下面内容: 1. 提供了同意设置颜色板的系统部件组件. 2. 为这些系统组件提供了触摸反馈动画. 3. Activity的过渡动画. 依据你的品牌标识,使用你所控制的颜色板能够自己定义材料主题的外观. 使用主题的属性能够给ActionBar 和 status bar进行着色. 系统部件拥有新的设计和触摸反馈动画.你能够为你的应用自己定义颜色板.触摸反馈动画以及Activity之间跳转的过渡

《Entity Framework 6 Recipes》中文翻译系列 目录篇 -持续更新

为了方便大家的阅读和学习,也是响应网友的建议,在这里为这个系列做一个目录.在目录开始这前,我先来回答之前遇到的几个问题. 1.为什么要学习EF? 这个问题很简单,项目需要.这不像学校,没人强迫你学习! 我学习EF的原因主要是: a.EF是微软推荐的数据库访问技术: b.能提高我的开发效率,我不喜欢写那密密麻麻的SQL: c.比我写的SQL更合理,更快.目前EF生成的SQL的质量已经很高了.你比较熟悉SQL的话,那它在速度上肯定比不上你,新手的话就别跟我争快慢了,能写一像样的SQL就不错了.至少我

8.Swift教程翻译系列——控制流之条件

3.条件语句 经常会需要根据不同的情况来执行不同的代码.你可能想要在发生错误的时候执行一段额外的代码,或者当某个值变得太高或者太低的时候给他输出出来.要实现这些需求,你可以使用条件分支. Swift提供两种方式来实现条件分支,也就是if语句和switch语句.一般来说If用在可能的情况比较少的简单条件中,当遇到复杂条件有很多种可能性的时候使用switch会更好,或者要根据模式匹配来判断要执行什么代码的时候switch也很有用. if语句 if的最简单形式只有一个单独的if条件,只有当条件为tru

使用Material Design 创建App翻译系列---列表和卡片集的创建

上一篇是使用Material Design 创建App翻译系列--材料主题的使用(Using Material Theme),进入正题: 想要在应用里创建Material Design风格的复杂列表和卡片,可以使用RecyclerView 和 CardView 控件. 创建列表 RecyclerView 控件是一个比ListView更加优越和灵活的控件.这个控件是一个通过有限个数的视图来显示大型数据集并能够高效滚动的容器. 当你有数据集合是基于用户动作或者网络事件而在运行时改变数据元素的时候你可

TypeScript手册翻译系列8-常见错误与Mixins

常见错误 下面列出了在使用TypeScript语言和编译器期间,经常会遇到的一些常见错误. "tsc.exe" exited with error code 1. 解决方案: 检查文件编码为UTF-8 - https://typescript.codeplex.com/workitem/1587 external module XYZ cannot be resolved 解决方案:检查模块路径是大小写敏感- https://typescript.codeplex.com/workit

7.Swift教程翻译系列——控制流之循环

英文版PDF下载地址http://download.csdn.net/detail/tsingheng/7480427 Swift提供了类C语言类似的控制流结构.包括for循环和while循环来多次执行任务,if和switch语句根据不同的条件执行不同的分支代码,break和continue语句将执行流程跳转到其他语句. 除了C里面传统的for-条件-递增循环,Swift还增加了for-in循环使得遍历数组,字典,范围,字符串或者其他序列都很简单. Swift的switch语句也要比C语言的sw

TypeScript手册翻译系列12-编写.d.ts文件

Writing .d.ts files When using an external JavaScript library, or new host API, you'll need to use a declaration file (.d.ts) to describe the shape of that library. This guide covers a few high-level concepts specific to writing definition files, the

RHadoop教程翻译系列 _Mapreduce(1)_第一个Mapreduce任务

如果单从概念上来说,Mapreduce和R中的函数lapply, tapply并无差别,它们都是把元素转化成列,然后计算索引(Mapreduce中的键),最后合并成一个定义好的组合.首先,让我们看一个简单的lappy的例子. small.ints = 1:1000 sapply(small.ints, function(x) x^2) 这个例子比较简单,只是计算了前1000个整数的平方,不过我们可以从这个例子中对lappy这个函数有个基本的认知,接下来关于这个函数还有更多有意思的例子.现在让我们

关于印发《新疆维吾尔自治区外语翻译系列专业技术职务任职资格条件(试行)》的通知

关于印发<新疆维吾尔自治区外语翻译系列专业技术职务任职资格条件(试行)>的通知 新人发[2003]74号 颁布时间:2003-10-21 00:00发文单位:新疆维吾尔自治区人事厅. 新疆维吾尔自治区外事办公室 伊犁哈萨克自治州人事局.外办,各地.州.市人事局.外办,自治区各委.办.厅.局.人民团体.大专院校.科研院所.大中型企业人事(职称)部门,中央驻疆单位人事(职称)部门: 现将<新疆维吾尔自治区外语翻译系列专业技术职务任职资格条件(试行)>印发给你们,请遵照执行.执行中有何意