使用UITableView+FDTemplateLayoutCell高度缓存以及实现文本展开全文和收起功能
cteq2260
8年前
<p>看到这个标题,我知道你肯定是想学习下如何实现UITableView的高度缓存或者是刚好有个需求是实现展开和收起的功能吧,下面我挨个来讲吧:</p> <p>*先说这个牛逼的UITableView+FDTemplateLayoutCell,先来说说怎么使用这个库吧,其实总结起来就两步:</p> <h3><strong>1.先注册cell(而且必须注册):</strong></h3> <pre> <code class="language-objectivec">[self.tableView registerClass:[CMDebunkCell class] forCellReuseIdentifier:@"CMDebunkCell"];</code></pre> <h3><strong>2.在返回tableView高度的代理方法中实现以下方法:</strong></h3> <pre> <code class="language-objectivec">CMEvent *model = [self.dataArray objectAtIndex:indexPath.row]; return [tableView fd_heightForCellWithIdentifier:@"CMDebunkCell" configuration:^(id cell) { CMDebunkCell * debunkCell = (CMDebunkCell *)cell; [debunkCell configCellWithModel:model indexPath:indexPath]; }];</code></pre> <p>总共就两步是不是很简单,但是有一个注意点也是使用这个库比较重要的地方:一定要在设置cell约束的时候给cell.contentView的底部约束设置好了,例如我在自定义的cell中这么写:</p> <pre> <code class="language-objectivec">[self.contentView mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.trailing.top.mas_equalTo(self); make.bottom.mas_equalTo(self.textView).offset(5); }];</code></pre> <p>这样我就确定了它的底部约束了,细心的你是不是已经发现貌似这和高度缓存没卵关系,好吧,那我下面就接着高度缓存的话题来说吧,刚刚在返回tableView高度的代理方法中我实现的一个方法是这个库自带的自动计算高度的方法,这个方法是没有带缓存功能的,它还提供了另外一个方法用与缓存高度:</p> <pre> <code class="language-objectivec">[tableView fd_heightForCellWithIdentifier:@"CMDebunkCell" cacheByKey:model.id configuration:^(id cell) { CMDebunkCell * debunkCell = (CMDebunkCell *)cell; [debunkCell configCellWithModel:model indexPath:indexPath]; }];</code></pre> <p>这里的cacheByKey是用与缓存的唯一标识,我这里的model.id是服务器返回给我的标识这一个cell的,当然作者还很贴心的提供了另外一个根据indexPath来缓存的方法:</p> <pre> <code class="language-objectivec">[tableView fd_heightForCellWithIdentifier:@"CMDebunkCell" cacheByIndexPath:indexPath configuration:^(id cell) { CMDebunkCell * debunkCell = (CMDebunkCell *)cell; [debunkCell configCellWithModel:model indexPath:indexPath]; }];</code></pre> <p>对于大多数人来说,使用以上两个方法就可以实现cell高度缓存以及自动计算行高的功能了,但是可能会有一些情形例如:当cell的高度是动态变化的怎么办呢?</p> <p>这就到了我们的第二个话题,那就是怎么实现展开和收起的功能,我先贴个效果图吧:</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/f035df7cccd6e32df4aeb081cbe8e17f.gif"></p> <p style="text-align:center">2016-07-21 16_21_36.gif</p> <p>从效果图可以看到,点击全文,文本会展开,点击收起文本自动缩回,当然高度肯定是要跟着变化的,下面贴上代码吧:</p> <pre> <code class="language-objectivec">//折叠或者展开按钮 self.foldBtn = [[UIButton alloc]init]; [self.contentView addSubview:self.foldBtn]; self.foldBtn.titleLabel.font = [UIFont systemFontOfSize:15]; self.foldBtn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; [self.foldBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; [self.foldBtn addTarget:self action:@selector(foldContentOrNo:) forControlEvents:UIControlEventTouchUpInside]; [self.foldBtn mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.content); make.top.equalTo(self.content.mas_bottom).offset(PADDINGMARGIN); make.width.mas_equalTo(100); make.height.mas_equalTo(30); }];</code></pre> <pre> <code class="language-objectivec">/** * 折叠或者展开事件 * * @param foldBtn 折叠或展开按钮 */ - (void)foldContentOrNo:(UIButton *)foldBtn { NSAssert(self.foldOrNoBlock !=nil, @"传入的折叠或者展开block不为nil"); self.foldOrNoBlock(foldBtn); }</code></pre> <p>self.foldOrNoBlock是我在自定义cell里面定义的回调block,它在控制器是这么写的:</p> <pre> <code class="language-objectivec">//折叠或展开block cell.foldOrNoBlock = ^(UIButton * foldBtn){ //获取所在按钮的cell CMDebunkCell * curCell = (CMDebunkCell *)[[foldBtn superview] superview]; //获取所在索引 NSIndexPath * indexPath = [weakSelf.tableView indexPathForCell:curCell]; model.is_Open = !model.is_Open; [weakSelf.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; };</code></pre> <p>在这个方法中你要注意的就是这个 model.is_Open,就是通过模型里面的is_Open这个属性来控制展开和收起的功能,下面就得说数据部分了,到底什么时候我们需要折叠呢,这个肯定是看你们的需求了,我这里就直接按字数来了,你也可以根据计算文本超过多少行之后来判断是收起还是展开,</p> <pre> <code class="language-objectivec">//文本内容 self.content.text = model.content.text; if (model.content.length >300) { self.foldBtn.hidden = NO; if (model.is_Open) { self.content.numberOfLines = 0; [_foldBtn setTitle:@"收起" forState:UIControlStateNormal]; }else { self.content.numberOfLines = 4; [_foldBtn setTitle:@"全文" forState:UIControlStateNormal]; } }else { self.content.numberOfLines = 0; self.foldBtn.hidden = YES; }</code></pre> <p>好吧,代码基本上就到这里了,上面的代码应该很容易看懂,当字数超过300(具体什么条件自己决定),根据模型的is_Open这个属性来决定展开和收起,展开和收起这里我是通过控制行数,当然你也可以通过限制它的高度来,SDAutoLayout这个库有兴趣可以去看看,它里面有个微信的demo就是根据控制高度来实现的,</p> <p>下面我觉得该说说重头戏了,当文本要展开时高度怎么变化,收起时又怎么变化?</p> <p>前面我们说了UITableView+FDTemplateLayoutCell这个分类牛逼的地方就在于自动计算行高了,如果我们在没有缓存的情况下,只要你使用了它其实高度的计算不需要我们来管,我们只需要[self.tableView reloadData]就完全足够了,但是如果有缓存的时候,这个问题就来了,你会发现,点击展开布局会乱,有一部分会看不到,这是因为高度并没有变化,一直用的是缓存的高度,所以解决办法如下(当然这是我的解决方案,有更好的,或者觉得我这个方案有问题也希望你能issue我):</p> <p>在返回高度的代理方法中,根据展开和收起的标识也就是上面提到的is_Open来决定返回哪个高度:</p> <pre> <code class="language-objectivec">//返回cell高度代理方法 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { CMEvent *model = [self.dataArray objectAtIndex:indexPath.row]; if (model.content.is_Open) { return [tableView fd_heightForCellWithIdentifier:@"CMDebunkCell" configuration:^(id cell) { CMDebunkCell * debunkCell = (CMDebunkCell *)cell; [debunkCell configCellWithModel:model indexPath:indexPath]; }]; }else { return [tableView fd_heightForCellWithIdentifier:@"CMDebunkCell" cacheByKey:model.id configuration:^(id cell) { CMDebunkCell * debunkCell = (CMDebunkCell *)cell; [debunkCell configCellWithModel:model indexPath:indexPath]; }]; } }</code></pre> <p> </p> <p> </p> <p>来自:http://www.jianshu.com/p/d84dc5051be0</p> <p> </p>