好久没有写简书了,虽然存了不少干货,但是整理起来还是好费劲.由于公司又要开发新的APP,在维护之前版本和开发新的版本之间,确实感觉一人有点乏力,有时候倍感身心疲惫,总想拖着理由,出去玩一趟.趁年轻!
好了废话不多说上放上效果图看一下
这个Demo是我用UITableView+FDTemplateLayoutCell进行改的,增加了点击全文改变当前Cell高度的小功能,原理都是按照FDTemplateLayoutCell的自适应高度进行做的,这个框架对于一些需要做自适应高度非常合适,而且自身又带有cell高度的缓存.
//我们只需要在这个方法写入如下代码,确定他的Identifier就能轻松对高度进行适配
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self.tableView fd_heightForCellWithIdentifier:@"feedCell" cacheByIndexPath:indexPath configuration:^(XQFeedCell *cell) {
// 在这个block中,重新cell配置数据源
[self setupModelOfCell:cell atIndexPath:indexPath];
}];
}
在约束上我用的是Masnory
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
[self createView];
[self setttingViewAtuoLayout];
}
return self;
}
#pragma make 创建子控件
- (void) createView {
YYLabel *titleLabel = [YYLabel new];
titleLabel.displaysAsynchronously = YES;
titleLabel.fadeOnAsynchronouslyDisplay = NO;
titleLabel.fadeOnHighlight = NO;
titleLabel.lineBreakMode = NSLineBreakByClipping;
titleLabel.textVerticalAlignment = YYTextVerticalAlignmentCenter;
[self.contentView addSubview:titleLabel];
self.titleLabel = titleLabel;
UILabel *contentLabel = [UILabel new];
contentLabel.numberOfLines = 0;
[self.contentView addSubview:contentLabel];
self.contentLabel = contentLabel;
UIView *taleView = [[UIView alloc] init];
taleView.backgroundColor = Color(240, 240, 240);
[self.contentView addSubview:taleView];
self.TalkView = taleView;
UILabel *talk = [UILabel new];
talk.numberOfLines = 0;
[taleView addSubview:talk];
self.talk = talk;
UIButton *flodbt = [[UIButton alloc]init];
flodbt.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
flodbt.titleLabel.font = [UIFont systemFontOfSize:15];
[flodbt setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[flodbt addTarget:self action:@selector(foldContentOr:) forControlEvents:UIControlEventTouchUpInside];
[taleView addSubview:flodbt];
self.Btn = flodbt;
UIImageView *contentImageView = [[UIImageView alloc] init];
contentImageView.contentMode = UIViewContentModeScaleAspectFit;
[self.contentView addSubview:contentImageView];
self.contentImageView = contentImageView;
YYLabel *userLabel = [YYLabel new];
userLabel.displaysAsynchronously = YES;
userLabel.fadeOnAsynchronouslyDisplay = NO;
userLabel.fadeOnHighlight = NO;
userLabel.lineBreakMode = NSLineBreakByClipping;
userLabel.textVerticalAlignment = YYTextVerticalAlignmentCenter;
[self.contentView addSubview:userLabel];
self.userLabel = userLabel;
YYLabel *timeLabel = [YYLabel new];
timeLabel.displaysAsynchronously = YES;
timeLabel.fadeOnAsynchronouslyDisplay = NO;
timeLabel.fadeOnHighlight = NO;
timeLabel.lineBreakMode = NSLineBreakByClipping;
timeLabel.textVerticalAlignment = YYTextVerticalAlignmentCenter;
[self.contentView addSubview:timeLabel];
self.timeLabel = timeLabel;
UIButton *flodbtn = [[UIButton alloc]init];
flodbtn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
flodbtn.titleLabel.font = [UIFont systemFontOfSize:15];
[flodbtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[flodbtn addTarget:self action:@selector(foldContentOrNo:) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:flodbtn];
self.foldBtn = flodbtn;
}
#pragma mark - 在此方法内使用 Masonry 设置控件的约束,设置约束不需要在layoutSubviews中设置,只需要在初始化的时候设置
- (void) setttingViewAtuoLayout {
int magin = 4;
int padding = 10;
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.and.left.mas_equalTo(self.contentView).offset(padding); // 设置titleLabel上边跟左边与父控件的偏移量
make.right.mas_equalTo(self.contentView.mas_right).offset(-padding); // 设置titleLabel右边与父控件的偏移量
}];
[self.contentLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.right.equalTo(self.titleLabel); // 设置contentLabel左边和右边对于titleLabel左右对齐
make.top.mas_equalTo(self.titleLabel.mas_bottom).offset(magin); // 设置contentLabel的上边对于titleLabel的下边的偏移量
}];
[self.foldBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.titleLabel);
make.top.equalTo(self.contentLabel.mas_bottom).offset(magin);
make.width.mas_equalTo(100);
make.height.mas_equalTo(30);
}];
[self.TalkView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.with.right.equalTo (self.titleLabel);
make.top.mas_equalTo(self.contentLabel.mas_bottom).offset(magin*2+30);
}];
[self.talk mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.and.left.mas_equalTo(self.TalkView).offset(padding);
make.right.mas_equalTo(self.TalkView.mas_right).offset(-padding);
make.bottom.mas_equalTo(self.TalkView.mas_bottom).offset(-40);
}];
[self.Btn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.talk);
make.top.equalTo(self.talk.mas_bottom).offset(padding);
make.width.mas_equalTo(100);
make.height.mas_equalTo(30);
}];
[self.contentImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(self.titleLabel.mas_centerX); // 设置contentImageView的左边对于titleLabel的左边对齐
make.top.mas_equalTo(self.TalkView.mas_bottom).offset(magin); // 设置contentImageView的上边对于contentLabel的下面的偏移量
}];
[self.userLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.titleLabel.mas_left); // 设置userLabel的左边对于titleLabel的左边对齐
make.top.mas_equalTo(self.contentImageView.mas_bottom).offset(magin); // 设置userLabel的上边对于contentImageView的下边的偏移量
make.bottom.mas_equalTo(self.contentView.mas_bottom).offset(-magin); // 设置userLabel的下边对于父控件的下面的偏移量
}];
[self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.and.top.equalTo(self.userLabel); // 设置timeLabel的上边与下边对于userLabel对齐
make.right.equalTo(self.titleLabel.mas_right); // 设置timeLabel的右边对于titleLabel的右边对齐
}];
}
以上是对它进行布局,在实现点击全文进行扩展和收起我用了Block
/**
* 折叠或者展开事件
*
* @param foldBtn 折叠或展开按钮
*/
- (void)foldContentOrNo:(UIButton *)foldBtn {
NSAssert(self.foldOrNoBlock !=nil, @"传入的折叠或者展开block不为nil");
self.foldOrNoBlock(foldBtn);
}
- (void)foldContentOr:(UIButton *)foldBtn {
NSAssert(self.ZfoldOrNoBlock !=nil, @"传入的折叠或者展开block不为nil");
self.ZfoldOrNoBlock(foldBtn);
}
为了避免cell复用出错,在model里面创建了一个Bool
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
XQFeedCell *cell = [tableView dequeueReusableCellWithIdentifier:@"feedCell"];
[self setupModelOfCell:cell atIndexPath:indexPath];
//折叠或展开block
cell.foldOrNoBlock = ^(UIButton * foldBtn){
//获取所在按钮的cell
XQFeedCell * cell = (XQFeedCell *)[[foldBtn superview] superview];
//获取所在索引
NSIndexPath * indexPath = [self.tableView indexPathForCell:cell];
Model *feed = self.feeds[indexPath.section][indexPath.row];
feed.is_Open = !feed.is_Open;
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
};
//折叠或展开block
cell.ZfoldOrNoBlock = ^(UIButton * foldBtn){
//获取所在按钮的cell
XQFeedCell * cell = [(XQFeedCell *)[[foldBtn superview] superview ] superview];
//获取所在索引
NSIndexPath * indexPath = [self.tableView indexPathForCell:cell];
Model *feed = self.feeds[indexPath.section][indexPath.row];
feed.is_open = !feed.is_open;
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
};
return cell;
}
然后在重写Model方法里面进行判断
if (feed.content.length >100) {
self.foldBtn.hidden = NO;
if (feed.is_Open) {
self.contentLabel.numberOfLines = 0;
[_foldBtn setTitle:@"收起" forState:UIControlStateNormal];
}else {
self.contentLabel.numberOfLines = 2;
[_foldBtn setTitle:@"全文" forState:UIControlStateNormal];
}
}else {
self.contentLabel.numberOfLines = 0;
self.foldBtn.hidden = YES;
}
if (feed.content.length >100) {
self.Btn.hidden = NO;
if (feed.is_open) {
self.talk.numberOfLines = 0;
[_Btn setTitle:@"收起" forState:UIControlStateNormal];
}else {
self.talk.numberOfLines = 2;
[_Btn setTitle:@"全文" forState:UIControlStateNormal];
}
}else {
self.talk.numberOfLines = 0;
self.Btn.hidden = YES;
}
这是我项目首页大部分都使用此框架.
最后献上次框架的GitHub链接https://github.com/forkingdog/UITableView-FDTemplateLayoutCell
-------------------------------分割线-------------------------------
由于最近iOS 10.3和Xcode 8.3的更新,默认会添加对应的宽度约束,从而导致了约束冲突.
demo 有会出现这种情况
cell. UITableViewCellContentView.width = 0 与
cell. UITableViewCellContentView.width = 375 (对应的最大宽度) MAXWidth
冲突
** FDTemplateLayoutCell ** layout cell created - FDFeedCell
2017-04-05 15:14:53.986190+0800 Demo[4230:1055755] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x174085eb0 UITableViewCellContentView:0x100422b60.width == 341 (active)>",
"<NSLayoutConstraint:0x17008f5a0 'UIView-Encapsulated-Layout-Width' UITableViewCellContentView:0x100422b60.width == 0 (active)>"
)
我在这个方法里面重写了并修复了这个问题,不过如果次框架更新最好还是用他更新过后的
- (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier cacheByIndexPath:(NSIndexPath *)indexPath configuration:(void (^)(id cell))configuration {
if (!identifier || !indexPath) {
return 0;
}
//获取缓存的模板单元格identifier。
UITableViewCell * cell = [self fd_templateCellForReuseIdentifier:identifier];
//手动调用以确保与实际单元格(屏幕上显示)一致的行为。
[cell prepareForReuse];
//自定义并为我们的模板单元格提供内容。
if(configuration){
configuration(cell);
}
CGFloat contentViewWidth = CGRectGetWidth(self.frame);
//如果单元格具有附件视图或系统附件类型,则其内容视图的宽度
// 比单元格的宽度小一些固定值。
if(cell.accessoryView){
contentViewWidth -= 16 + CGRectGetWidth(cell.accessoryView.frame);
} else {
static CGFloat systemAccessoryWidths [] = {
[UITableViewCellAccessoryNone] = 0,
[UITableViewCellAccessoryDisclosureIndicator] = 34,
[UITableViewCellAccessoryDetailDisclosureButton]= 68,
[UITableViewCellAccessoryCheckmark] = 40,
[UITableViewCellAccessoryDetailButton] = 48
};
contentViewWidth -= systemAccessoryWidths [cell.accessoryType];
}
CGSize fittingSize = CGSizeZero;
//如果启用自动布局,单元格的contentView必须有一些约束。
BOOL autoLayoutEnabled = cell.contentView.constraints.count> 0 &&!cell.fd_enforceFrameLayout;
if(autoLayoutEnabled){
if (IOS_VERSION > 10.2) {
[cell.contentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(0).priorityLow();
make.right.mas_equalTo(0).priorityLow();
}];
}
NSLayoutConstraint *tempWidthConstraint =
[NSLayoutConstraint constraintWithItem:cell.contentView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:contentViewWidth];
[cell.contentView addConstraint:tempWidthConstraint];
// Auto layout engine does its math
fittingSize = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
NSLog(@"fittingSize === %@",NSStringFromCGSize(fittingSize));
[cell.contentView removeConstraint:tempWidthConstraint];
} else {
// If not using auto layout, you have to override "-sizeThatFits:" to provide a fitting size by yourself.
// This is the same method used in iOS8 self-sizing cell's implementation.
// Note: fitting height should not include separator view.
SEL selector = @selector(sizeThatFits:);
BOOL inherited = ![cell isMemberOfClass:UITableViewCell.class];
BOOL overrided = [cell.class instanceMethodForSelector:selector] != [UITableViewCell instanceMethodForSelector:selector];
if (inherited && !overrided) {
NSAssert(NO, @"Customized cell must override '-sizeThatFits:' method if not using auto layout.");
}
fittingSize = [cell sizeThatFits:CGSizeMake(contentViewWidth, 0)];
}
//如果需要,为分隔线添加1px额外空间,模拟默认的UITableViewCell。
if
(self.separatorStyle!= UITableViewCellSeparatorStyleNone){
fittingSize.height += 1.0 / [UIScreen mainScreen] .scale;
}
if(autoLayoutEnabled){
[self fd_debugLog:[NSString stringWithFormat:@"calculate using auto layout - %@",@(fittingSize.height)]];
} else {
[self fd_debugLog:[NSString stringWithFormat:@"计算使用框架布局 - %@",@(fittingSize.height)]];
}
return fittingSize.height;
}