我有一个包含大图像的桌面视图,可以填充单元格,并根据图像大小设置行高。不幸的是,当滚动到下一个单元格时,表格严重抖动。
我被告知如果我在将行加载到表格之前缓存行高和图像,我的tableview将更加流畅地滚动。 我的所有数据都存储在plist中。
我如何进行缓存? 代码是什么样的,它在哪里?
谢谢!
这是我加载图片的代码:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *detailTableViewCellIdentifier = @"Cell";
DetailTableViewCell *cell = (DetailTableViewCell *)
[tableView dequeueReusableCellWithIdentifier:detailTableViewCellIdentifier];
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"DetailTableViewCell" owner:self options:nil];
for(id currentObject in nib)
{
cell = (DetailTableViewCell *)currentObject;
}
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *Path = [[NSBundle mainBundle] bundlePath];
NSString *MainImagePath = [Path stringByAppendingPathComponent:([[appDelegate.sectionsDelegateDict objectAtIndex:indexPath.section] objectForKey:@"MainImage"])];
cell.mainImage.image = [UIImage imageWithContentsOfFile:MainImagePath];
return cell;
}
我还使用以下方法计算行高:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
AppDelegate *appDelegate = (DrillDownAppAppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *Path = [[NSBundle mainBundle] bundlePath];
NSString *MainImagePath = [Path stringByAppendingPathComponent:([[appDelegate.sectionsDelegateDict objectAtIndex:indexPath.section] objectForKey:@"MainImage"])];
UIImage *imageForHeight = [UIImage imageWithContentsOfFile:MainImagePath];
imageHeight = CGImageGetHeight(imageForHeight.CGImage);
return imageHeight;
}
编辑:这是下面的最终代码。
#define PHOTO_TAG 1
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Photo";
UIImageView *photo;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
UIImage *theImage = [UIImage imageNamed:[[appDelegate.sectionsDelegateDict objectAtIndex:indexPath.section] objectForKey:@"MainImage"]];
imageHeight = CGImageGetHeight(theImage.CGImage);
imageWidth = CGImageGetWidth(theImage.CGImage);
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
photo = [[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, imageWidth, imageHeight)] autorelease];
photo.tag = PHOTO_TAG;
[cell addSubview:photo];
} else {
photo = (UIImageView *) [cell viewWithTag:PHOTO_TAG];
[photo setFrame:CGRectMake(0, 0, imageWidth, imageHeight)];
}
photo.image = theImage;
return cell;
}
答案 0 :(得分:16)
缓存不是tableview性能的灵丹妙药。缓存只有在计算成本很高的情况下才有价值,您可以避免计算它。另一方面,如果您在UITableViewCell中只有太多的视图,那么缓存对您无效。如果您的行高度都相同,则无需缓存。如果您使用+[UIImage imageNamed:]
,则系统已经为您缓存了图像。
UITableViewCells最常见的一阶问题是在其中放置了太多的子视图。你是如何构建你的细胞的?您是否花时间研究表视图编程指南,尤其是A Closer Look at Table-View Cells?理解这份文件可以为你节省更多的悲伤。
编辑:(基于上面的代码)
首先,您正在获取可重复使用的单元格,然后立即将其丢弃,读取NIB并迭代所有顶级对象以查找单元格(一个看起来几乎与您刚扔掉的单元格相同)。然后你计算出一个字符串,用于打开文件并读取内容。每次UITableView想要一个新的单元格时都会这样做。你会一遍又一遍地为同一行做这件事。
然后,当UITableView想要知道高度时,再次从磁盘读取图像。并且每次UITableView请求时都会这样做(并且它可能会多次询问同一行,但它会尝试对此进行优化)。
您应该首先阅读上面链接的UITableView编程指南。希望能帮助很多。当你这样做时,你应该考虑以下事项:
您表示此单元格中只有一个图像视图。你真的需要一个NIB吗?如果您坚持使用NIB(并且在某些情况下有理由使用它们),请阅读编程指南,了解如何实现基于NIB的单元。您应该使用IBOutlet
,而不是尝试迭代顶级对象。
+[UIImage imageNamed:]
将自动查找Resources目录中的文件,而无需计算bundle的路径。它还会自动缓存这些图像。
-dequeueReusableCellWithIdentifier:
的目的是获取UITableView不再使用的单元格,并且您可以重新配置而不是创建新单元格。你在叫它,但是你马上把它丢掉了。您应该检查它是否返回nil,并且只在NIB中加载它。否则,您只需要更改图像。再次阅读编程指南;它有很多这方面的例子。只需确保您真正尝试了解-dequeueReusableCellWithIdentifier:
正在做什么,并且不要将其视为您在程序中此处键入的内容。
答案 1 :(得分:5)
如果你确实需要缓存高度,我做了类似的事情(显示“文章”对象的单元格的缓存高度 - 文章可能是几个子类中的一个):
+ (CGFloat) heightForArticle: (Article*) article atWidth: (CGFloat) width {
static NSCache* heightCache = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
heightCache = [NSCache new];
});
NSAssert(heightCache, @"Height cache must exist");
NSString* key = @"unique"; //Create a unique key here
NSNumber* cachedValue = [heightCache objectForKey: key];
if( cachedValue )
return [cachedValue floatValue];
else {
CGFloat height = 40;//Perform presumably large height calculation here
[heightCache setObject: [NSNumber numberWithFloat: height] forKey: key];
return height;
}
}