首页 > 代码库 > Fantageek翻译系列之《使用Autolayout显示变化高度的UITableViewCell》

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 : UITableViewCell2 @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)layoutSubviews12 {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。

 

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