【问题标题】:How do I cache something for a tableview?如何为 tableview 缓存一些东西?
【发布时间】:2009-09-03 02:57:21
【问题描述】:

我有一个表格视图,其中包含填充单元格的大图像,并且行高是根据图像大小设置的。不幸的是,当滚动到下一个单元格时,表格抖动得很厉害。

有人告诉我,如果我在将行高和图像加载到表格中之前缓存它们,我的表格视图将滚动更顺畅。 我所有的数据都存储在一个 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;
 }

【问题讨论】:

    标签: iphone caching uitableview


    【解决方案1】:

    缓存并不是 tableview 性能的灵丹妙药。缓存仅在计算成本高昂的情况下才有价值,并且您可以避免计算它。另一方面,如果您只是在 UITableViewCell 中有太多视图,那么缓存对您没有任何作用。如果您的行高都相同,那么就没有什么可以缓存的了。如果你使用+[UIImage imageNamed:],那么系统已经在为你缓存图片了。

    UITableViewCells 最常见的一阶问题是在其中放置了太多的子视图。你是如何构建你的细胞的?你有没有花时间学习表格视图编程指南,特别是A Closer Look at Table-View Cells?了解这份文件,以后会省很多事。

    编辑:(基于上面的代码)

    首先,您要获取一个可重复使用的单元格,然后立即将其丢弃,读取一个 NIB 并遍历所有顶级对象以寻找一个单元格(一个看起来几乎与您刚刚丢弃的单元格完全相同的单元格)。然后你计算出一个字符串,你用它来打开一个文件并读取内容。每次 UITableView 需要一个新单元格时,您都会这样做,这很多。而且你一遍又一遍地为相同的行做。

    然后,当 UITableView 想知道高度时,你再次从磁盘读取图像。并且每次 UITableView 询问时您都会这样做(它可能会多次询问同一行,尽管它确实会尝试对此进行优化)。

    您应该从阅读上面链接的 UITableView 编程指南开始。希望这会有所帮助。完成后,您应该考虑以下几点:

    • 您指出此单元格中只有一个图像视图。你真的需要一个NIB吗?如果您确实坚持使用 NIB(并且在某些情况下有理由使用它们),那么请阅读有关如何实现基于 NIB 的单元的编程指南。您应该使用IBOutlet,而不是尝试遍历顶级对象。

    • +[UIImage imageNamed:] 将自动在您的资源目录中查找文件,而无需您计算包的路径。它还会自动缓存这些图片。

    • -dequeueReusableCellWithIdentifier: 的重点是获取 UITableView 不再使用的单元格,您可以重新配置而不是创建一个新单元格。您正在调用它,但您立即将其丢弃。您应该检查它是否返回 nil,如果返回,则仅将其从 NIB 中加载。否则,您只需要更改图像。再次阅读编程指南;它有很多很多这样的例子。只需确保您真正尝试了解 -dequeueReusableCellWithIdentifier: 正在做什么,不要将其视为您在程序中此时键入的内容。

    【讨论】:

    • 罗布,感谢您的回复。我只有一个单元格视图,那是一个图像。我从上一个问题中得到了缓存图像的想法:stackoverflow.com/questions/1352479/…
    • rpetrich 的回答很好。关于缓存 rowHeight,最简单的解决方案是维护一个 NSNumber 数组,用于存储每行的计算高度。您可以先让它们都为 0,如果为 0,则计算它并将结果存储在数组中。如果它不为零,则返回它。不过,在 Instruments 中进行一些分析,看看在进行太多随机更改之前您真正花费时间的地方。行高计算可能是您最不关心的问题。也许您过于频繁地缩放图像(似乎很有可能)。仪器会有所帮助。
    • 哦,哇....是的,这会很慢....您目前正在绕过 iPhone 提供的所有优化:D 我需要编辑我上面的答案以提供更好的细节.我很震惊,它只是有时有点生涩。哇。这做了很多工作。
    • 我会用仪器检查一下。为了提高性能,我没有缩放图像。在加载到程序之前,它们都已设置为适当的大小。
    • 期待您的想法!我在另一个我尝试过的答案中发布了一些代码,但没有太大成功。再次感谢您的帮助!
    【解决方案2】:

    如果您确实需要缓存高度,我做了这样的事情(缓存显示“文章”对象的单元格的高度 - 文章可能是几个子类之一):

    + (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;
        }
    }
    

    【讨论】:

    • 这对我很有帮助,我用它来缓存高度并缓存我的表格视图单元格图像。谢谢:)
    • 实际上是尊重,但我收回我的评论。这不起作用,我注意到有时它会返回错误的高度值。所以细胞有时会变得太大或太小。
    猜你喜欢
    • 2019-03-16
    • 1970-01-01
    • 2011-05-17
    • 2012-03-03
    • 1970-01-01
    • 2014-05-12
    • 2017-06-26
    • 2011-04-04
    • 2020-03-13
    相关资源
    最近更新 更多