Masonry+FDTemplateLayoutCell 实现Cell自动布局


在经过frame时代的布局计算后,目前进入了autolayout 布局。相对之前frame布局的算算算,autolayout时代则要好的多,只需要计算控件之间的相对距离。因此布局简洁了不少。


传统的用代码设置autolayout十分复杂,因此这里我们使用一款叫做Masonry的开源框架,它封装了IOS原生代码设置约束的方法。使我们能够更简便的使用代码进行autolayout布局。

 下载地址:https://github.com/SnapKit/Masonry。

既然上面已经介绍了用代码设计的框架,这里我们还需要一个根据约束来对cell进行计算高度的框架.FDTemplateLayoutCell.

下载地址https://github.com/forkingdog/UITableView-FDTemplateLayoutCell

好了废话不多说我们开始今天的项目。

实验目的

1.使用masonry 来给一个表格中的cell设置约束。并且使用FDTemplateLayoutCell来对设置好约束的cell进行行高激素啊。

实验效果 

图一  当文本行高度小于头像的高度时。






图二 当文本行高度大于头像的高度时




实现方法。

首先我们创建一个cell,并且使用Masonry进行代码约束设置。代码如下

- (void) setupLayout {
 __weaktypeof(self) weakSelf =self;
 UIImageView*contentImageView = [[UIImageViewalloc]init];
 // contentImageView.contentMode = UIViewContentModeScaleAspectFit;
 [self.contentViewaddSubview:contentImageView];
 self.imageV= contentImageView;
 UIImageView*headImageV = [[UIImageViewalloc]init];
 [self.contentViewaddSubview:headImageV];
 self.headImageV= headImageV;
 UILabel* contentLabel = [[UILabelalloc]init];
 contentLabel.font= [UIFontsystemFontOfSize:13.0f];
 contentLabel.textColor= [UIColorredColor];
 contentLabel.numberOfLines=0;
 contentLabel.textAlignment=NSTextAlignmentNatural;
 self.contentLabel= contentLabel;
 [self.contentViewaddSubview:contentLabel];
 [contentImageViewmas_makeConstraints:^(MASConstraintMaker*make) {
 make.top.equalTo(weakSelf.contentView.mas_top).offset(0);
 make.left.equalTo(weakSelf.contentView.mas_left).offset(0);
 make.right.equalTo(weakSelf.contentView.mas_right).offset(0);
 //make.height.equalTo(weakSelf.contentView.mas_width).multipliedBy(0.5);
 make.bottom.equalTo(headImageV.mas_top).offset(-10);
 }];
 [headImageVmas_makeConstraints:^(MASConstraintMaker*make) {
 make.size.mas_equalTo(CGSizeMake(60,60));
 make.left.equalTo(weakSelf.contentView.mas_left).offset(10);
 make.top.equalTo(contentImageView.mas_bottom).offset(10);
 // make.bottom.equalTo(weakSelf.contentView.mas_bottom).offset(-10);
 make.bottom.equalTo(weakSelf.contentView.mas_bottom).offset(-10).priorityMedium(0);
 }];
 [contentLabelmas_makeConstraints:^(MASConstraintMaker*make) {
 make.top.equalTo(contentImageView.mas_bottom).offset(10);
 make.left.equalTo(headImageV.mas_right).offset(10);
 make.right.equalTo(weakSelf.contentView.mas_right).offset(-10);
 make.bottom.equalTo(weakSelf.contentView.mas_bottom).offset(-10).priorityHigh(0);
 }];
 }
 这里要注意的是,我们添加的控件是添加到了cell中的cotentView里面,因此,再进行父类约束设置的时候,就必须用contentView来进行参照,而不是cell中的View. 比如设置一个UIImageView 它的在其父类视图中,上下左右的间距都是0,它在父视图中展示的效果就应该是一个,铺满父视图的视图。但是如果你添加到contentView上面,却用的是View来进行约束参照。约束就不会起作用,FDTemplateLayoutCell也不会计算出准确的高度。
 在设置好约束以后,因为这些控件都是木有数据的,所以现在就只有约束,当然,光有约束我们没有数据,所以我们还是看不出来布局效果。
 为了让我们看出布局的效果,所以我们就要为这些控件提供一个写入数据的接口。
 - (void)setObject:(entity*)object {
 _object= object;
 [_imageVsetImage:[UIImageimageNamed:@"contentImage"]];
 [_headImageVsetImage:[UIImageimageNamed:@"headImage"]];
 [_contentLabelsetText:@"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"];
 }
 这样一来我们的控件里面也有数据了,下面我们就要用FDTemplateLayoutCell来自动计算我们的cell的高度了。
 控制器中的实现代码如下。
 - (NSInteger) tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
 returnself.dataArray.count;
 }
 - (CGFloat) tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath {
 return[tableViewfd_heightForCellWithIdentifier:@"masoryCell"cacheByIndexPath:indexPathconfiguration:^(masoryCell* cell) {
 //配置cell的数据源,和"cellForRow"干的事一致,比如:
 cell.object=self.dataArray[indexPath.row];
 }]+20;
 }
 - (UITableViewCell*) tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
 masoryCell* cell =[tableViewdequeueReusableCellWithIdentifier:@"masoryCell"forIndexPath:indexPath];
 // UITableViewCell* cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"customeTableViewCell"];
 cell.object=self.dataArray[indexPath.row];
 returncell;
 }
 通过FDTemplateLayoutCell 提供的分类方法
 [tableViewfd_heightForCellWithIdentifier:@"masoryCell"cacheByIndexPath:indexPathconfiguration:^(masoryCell* cell) {
 //配置cell的数据源,和"cellForRow"干的事一致,比如:
 cell.object=self.dataArray[indexPath.row];
 }];

就可以为我们计算出cell的高度了

这里要注意的是,必须注册我们的设好的cell到talbeView中 ,这样我们才能复用,这些cell.

提示:在使用masonry为cell设置约束的时候,必须在cell初始化的时候实现,不能在写入数据的时候,为cell设置约束。不然会导致计算不出高度。

为了达到上图中的效果。

当Label的行高大于Image头像的时候,Label的设置的约束起作用。当Label小于头像图片的时候,就头像设置的约束起作用。

这里给出的解决办法是设置约束优先级。autoLayout 约束优先级,工作的原理是这样的:如上图中所示:头像的上约束是,相对于大图的底部间隔10的单位,下约束是相对于contentView底部间隔10个单位。同样Label设置的上下约束也是和头像视图是一致的。所以我们这里设置label的下约束优先级高于头像的下约束,这里头像视图的大小是固定是60x60。而Label高度又是不固定的,所以系统先判断,如果label的行高小于或者等于头像视图的时候。系统默认两个视图的下约束不冲突。各自按照设置好的约束进行设置,所以头像视图的高度等于label视图的高度,因此头像视图距cotentView视图底部为10个单位。但是一旦label的高度大于头像视图的高度,则系统判断优先级更高的label视图的下约束生效。所以就让label的下约束生效。因此label的底部距离contentView视图的底部为10个单位。而头像视图的下约束变为距离contentView底部Label的高度 减去 头像视图的高度 再加上10个单位的距离。