问题:我遇到的问题是使用estimatedHeightForRowAtIndexPath和heightForRowAtIndexPath计算的单元格高度返回错误的高度。具体而言,有时单行文本标签的高度返回与贴纸相同的高度(高度太大)。或者,图像的高度与单行文本标签的高度相同(高度太小)。
理论:我使用不同的单元格高度,具体取决于它的消息类型。因为有时文本显示为与贴纸(固定高度为150)相同的高度,我想知道在某些地方是否存在重复使用细胞的错误。或者,我从单元格高度缓存中保存/读取的方式存在一个错误。
奖励:我花了至少一个月的时间试图找出正在发生的事情。这不是一个容易的问题。当我可以添加它时,你会收到我不朽的感激之情和赏金。
这是一些上下文的代码。首先,heightForRowAtIndexPath:
- (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) indexPath {
NSNumber *cachedCellHeight = [self.cachedCellHeights objectForKey:[NSString stringWithFormat:@"%u", indexPath.row]];
if([cachedCellHeight floatValue] >= MINIMUM_CELL_HEIGHT) {
// NSLog(@"Retrieving cached cell height (estimatedHeightForRowAtIndexPath): %f for row: %u", [cachedCellHeight floatValue], indexPath.row);
return [cachedCellHeight floatValue];
}
// Cell height is not in the cache (or somehow an invalid height is retrieved), calculate and store it
NSManagedObject *record = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSInteger type = [[record valueForKey:@"type"] integerValue];
NSString *message = [NSString stringWithFormat:@"%@", [record valueForKey:@"message"]];
static NSString *identifier = @"MessagesTableViewCellTextLeft";
switch (type) {
case TypeText:
identifier = @"MessageTypeText";
break;
case TypeImage:
identifier = @"MessageTypeImage";
break;
case TypeSticker:
identifier = @"MessageTypeSticker";
break;
}
static MessagesTableViewCell *cell = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cell = [self.tableView dequeueReusableCellWithIdentifier: identifier];
});
CGFloat calculatedCellHeight;
UIImage *image = nil;
switch (type) {
case TypeText:
cell.textLabel.text = message;
[cell setNeedsLayout];
[cell layoutIfNeeded];
calculatedCellHeight = [cell.contentView systemLayoutSizeFittingSize: UILayoutFittingCompressedSize].height; // used for multi line labels
break;
case TypeImage:
image = [self getImageFromDocumentsFolder:message];
if (image) {
calculatedCellHeight = [self getImageCellHeight: image]; // calculates height based on aspect ratio of image
}
break;
case TypeSticker:
calculatedCellHeight = 150; // will always be this height
break;
}
/* If the calculation fails and the value is below the minimum cell height, don't store in the cell heights cache */
if(calculatedCellHeight >= MINIMUM_CELL_HEIGHT) {
// NSLog(@"Calculating and storing cell height (estimatedHeightForRowAtIndexPath): %f for row: %u for type: %@", calculatedCellHeight, indexPath.row, identifier);
[self.cachedCellHeights setObject:[NSNumber numberWithFloat:calculatedCellHeight] forKey:[NSString stringWithFormat:@"%u", indexPath.row]];
[self saveCachedHeightsToDisk];
} else {
calculatedCellHeight = [cell.contentView systemLayoutSizeFittingSize: UILayoutFittingCompressedSize].height;
}
return calculatedCellHeight;
}
另外,要使多行标签生效,我必须在自定义单元格类中设置preferredMaxLayoutWidth:
- (void) updateConstraints {
[super updateConstraints];
// For multi-line labels, this property is required to word wrap appropriately
// In this case, word wrapping will occur 100 units away from screen edge
textLabel.preferredMaxLayoutWidth = CGRectGetWidth([[UIScreen mainScreen] bounds]) - 100;
}
这是我对单元格高度缓存的实现。注意,cachedCellHeights只是一个综合的NSDictionary:
- (void) saveCachedHeightsToDisk {
NSArray *directoryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if ([directoryPath count] > 0) {
NSString *rebasedPath = [ClientUtilities rebasePathToCurrentDocumentPath:CACHED_CELL_HEIGHTS_FILENAME];
[self.cachedCellHeights writeToFile:rebasedPath atomically:YES];
}
}
- (NSMutableDictionary *) loadCachedHeightsFromDisk {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *filePath = [ClientUtilities rebasePathToCurrentDocumentPath:CACHED_CELL_HEIGHTS_FILENAME];
if (![fileManager fileExistsAtPath:filePath]){
// NSLog(@"Initializing cached cell heights to disk");
return [NSMutableDictionary dictionary];
} else {
// NSLog(@"Loading cached cell heights from disk");
return [NSMutableDictionary dictionaryWithContentsOfFile:filePath];
}
}
注意,estimatedHeightForRowAtIndexPath和heightForRowAtIndexPath使用相同的代码。我知道你不应该在估计的高度做任何繁重的计算。但是,由于我从底部堆叠单元格并且它们的高度可能会大幅度变化,所以我必须进行此计算(或从缓存中读取)。否则滚动到底部功能不起作用(它将滚动放在它认为结束是基于错误的估计高度计算,这是一些无意义的随机点)。
非常感谢解决方案或任何建议或建议。
答案 0 :(得分:1)
好的,所以缓存的东西是红鲱鱼。这不是我的缓存。我确定。
我发现导致细胞高度高于应该高的有问题的块:
cell.textLabel.text = message;
[cell setNeedsLayout];
[cell layoutIfNeeded];
calculatedCellHeight = [cell.contentView systemLayoutSizeFittingSize: UILayoutFittingCompressedSize].height;
NSLog(@"Calculated cell height is: %f", calculatedCellHeight);
UILayoutFittingCompressedSize返回的高度不正确。一旦它进入这个“状态”,所有文本单元的评估大小为150.无论我添加多少次新文本单元格。如果我强行关闭并返回应用程序,则可以解决问题。
我仍然无法弄清楚为什么会这样。线程问题?细胞重用问题?有什么建议吗?