今天有用户反馈说10.3的系统,有些文字显示不全,影响正式用户我哪里敢怠慢。急速的更新手机系统进行测试,发现真的是有问题,而且这个问题是UITableView+FDTemplateLayoutCell 引起的,计算的高度不准确引起的。
经过测试发现问题出现在xib或者nib创建的cell 拉约束之后再用UITableView+FDTemplateLayoutCell 计算高度就会出现问题,用Masonry 配合UITableView+FDTemplateLayoutCell 使用没问题(至少我的是没有问题,当然如果你的有问题也可以看下我列举的解决办法,相信有适合你的)
经过Gogle 发现目前为止很少有人提到这个问题(难道大家都没发现还是大家的都没问题,当然也有可能没用UITableView+FDTemplateLayoutCell 的),在UITableView+FDTemplateLayoutCell issues 和 Masonry issues 里面有很多提到这个问题的。究其原因好像iOS 10.3 会加一个宽一个高约束(Looks like iOS 10.3 has two additional constraints there for width/height),对Autolayout的约束有新的计算方式。
这个方法亲测是可以的,但是有个问题nib 创建的cell 很多都不知道这个值到底是多少,就是知道也不能一个cell一个cell 的设置吧!至少我是不愿意 ,天啊几十种cell啊!当然有些人可能会图省事,随意设置一个吧,label.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 80 这样你测试的是会发现确实换行了,但是真的行吗?label 的换行是要根据这个 preferredMaxLayoutWidth 类似计算文字高度的方法
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(nullable NSDictionary*)attributes context:(nullable NSStringDrawingContext *)context NS_AVAILABLE(10_11, 7_0)
就像这个方法中size size的宽就是要设置label能够显示的宽,如果给的不对当然计算出的高度也会不对,如果给的小了计算的高度就高,给的大了计算的高度就低
因为有时候我发现第一次label显示的是没问题的,但是刷新一下就不行了,所以我想到在刷新重新算高度之前刷新下约束,这样就可以知道label的最大宽度限制了,当然我也不想在所以的cell 里面处理 所以在UITableView+FDTemplateLayoutCell 里面做了些处理
- (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier configuration:(void (^)(id))configuration { if (!identifier) { return 0; } // Fetch a cached template cell for `identifier`. UITableViewCell *cell = [self fd_templateCellForReuseIdentifier:identifier]; // Manually calls to ensure consistent behavior with actual cells (that are displayed on screen). [cell prepareForReuse]; // Customize and provide content for our template cell. if (configuration) { configuration(cell); } CGFloat contentViewWidth = CGRectGetWidth(self.frame); // If a cell has accessory view or system accessory type, its content view's width is smaller // than cell's by some fixed values. 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; // If auto layout enabled, cell's contentView must have some constraints. BOOL autoLayoutEnabled = cell.contentView.constraints.count > 0 && !cell.fd_enforceFrameLayout; if (autoLayoutEnabled) { // Add a hard width constraint to make dynamic content views (like labels) expand vertically instead // of growing horizontally, in a flow-layout manner. if (IOS_VERSION > 10.2) { [cell layoutIfNeeded]; } 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]; [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)]; } // Add 1px extra space for separator line if needed, simulating default 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:@"calculate using frame layout - %@", @(fittingSize.height)]]; } return fittingSize.height; }
*重点在这里
1.png
当然如果这个能满足你也是很好的,但是却满足不了我的工程,这个是可以完全解决nib 创建的cell 的高度问题,但是纯代码用Masonry加约束的却出现了问题,反而不能换行了,所以这个方法也不适合我,此路不通我再想他法继续往下看
在这里我这样理解的,既然xcode自动帮我们加的左右约束有问题,那我不用他的了, 我自己加
2.png
在同样的地方替换掉layoutIfNeeded,换成加左右约束, 记得后面加上priorityLow ,这样是避免跟cell 里面手动加的约束起冲突,这样就技能满足nib cell 也能满足纯代码cell ,也不用一个cell 一个cell 的改。至此这个问题完美的解决了(我的问题是解决了,你的解决了吗?欢迎留言共同探讨,小牛路过,不喜勿喷!)
什么,你还懒得敲,要我发源码!好吧忍不了你了
- (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier configuration:(void (^)(id))configuration { if (!identifier) { return 0; } // Fetch a cached template cell for `identifier`. UITableViewCell *cell = [self fd_templateCellForReuseIdentifier:identifier]; // Manually calls to ensure consistent behavior with actual cells (that are displayed on screen). [cell prepareForReuse]; // Customize and provide content for our template cell. if (configuration) { configuration(cell); } CGFloat contentViewWidth = CGRectGetWidth(self.frame); // If a cell has accessory view or system accessory type, its content view's width is smaller // than cell's by some fixed values. 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; // If auto layout enabled, cell's contentView must have some constraints. BOOL autoLayoutEnabled = cell.contentView.constraints.count > 0 && !cell.fd_enforceFrameLayout; if (autoLayoutEnabled) { // Add a hard width constraint to make dynamic content views (like labels) expand vertically instead // of growing horizontally, in a flow-layout manner. 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]; [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)]; } // Add 1px extra space for separator line if needed, simulating default 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:@"calculate using frame layout - %@", @(fittingSize.height)]]; } return fittingSize.height; }