如何为tableview缓存一些东西?

时间:2009-09-03 02:57:21

标签: iphone caching uitableview

我有一个包含大图像的桌面视图,可以填充单元格,并根据图像大小设置行高。不幸的是,当滚动到下一个单元格时,表格严重抖动。

我被告知如果我在将行加载到表格之前缓存行高和图像,我的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;
 }

2 个答案:

答案 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;
    }
}