ios - 从 NSData 加载 UIImage 导致不稳定的 UITableView 滚动

标签 ios objective-c xcode uitableview nsdata

从 NSData 加载图像时,我正在让我的 tableview 平滑滚动。

一些背景……我将图像 NSData 存储在核心数据中(使用“允许外部存储”选项,所以我不认为存储 URL 或类似的东西是这里的解决方案)。此外,我存储了两种类型的数据,一种是高分辨率的 UIImagePNGRepresentation,另一种是低分辨率的 UIImageJPGRepresentation。我正在为我的表格 View 加载低分辨率图像。入口对象的图像属性不存储在核心数据中,它是在事后设置的。这是我的做法:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{        

    DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:@"LogCell" forIndexPath:indexPath];

    [cell setGradients:@[[UIColor whiteColor], [UIColor lightTextColor]]];
    Entry *entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
    UIImage *entryImage = entry.image;

    if (entryImage)
        cell.rightImageView.image = entryImage;
    else {
        NSOperationQueue *oQueue = [[NSOperationQueue alloc] init];
        [oQueue addOperationWithBlock:^(void) {
            UIImage *image = [UIImage imageWithData:entry.photo.lowResImageData];
            if (!image) image = [UIImage imageNamed:@"defaultImage.png"];
            entry.image = image;
        }];
        [oQueue addOperationWithBlock:^(void) {
            [[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
                DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
                cell.rightImageView.image = entry.image;
            }];
        }];

    }

    return cell;
}

关于如何让它更顺利地运行有什么想法吗?非常感谢任何帮助!

谢谢

更新

这让我更接近了,但仍然有一点紧张。 . . (在 cellForRowAtIndexPath 内)

    UIImage *entryImage = entry.image;

    if (entryImage)
        cell.rightImageView.image = entryImage;
    else {
        [self.imageQueue addOperationWithBlock:^(void) {
            UIImage *image = [UIImage imageWithData:entry.photo.lowResImageData];
            if (!image) image = [UIImage imageNamed:@"bills_moduleIcon.png"];
            entry.image = image;
            [entry.image preload];

            [[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
                DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
                cell.rightImageView.image = entry.image;
            }];
        }];
    }

该预加载方法位于 UIImageView 的类别中,如下所示:

- (void)preload
{
    CGImageRef ref = self.CGImage;
    size_t width = CGImageGetWidth(ref);
    size_t height = CGImageGetHeight(ref);
    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, width * 4, space, kCGImageAlphaPremultipliedFirst);
    CGColorSpaceRelease(space);
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), ref);
    CGContextRelease(context);
}

我认为我的下一个攻击计划是弄乱存储图像的大小(如评论中所述),或者尝试在表格滚动时不更新 imageView。非常感谢对这两种方法的任何想法!我完成后请回来查看

更新 2 非常感谢所有帮助我解决这个问题的人!这是最终的解决方案(在 cellForRowAtIndexPath 中:

    if (entryImage)
        cell.rightImageView.image = entryImage;
    else {
        [self.imageQueue addOperationWithBlock:^(void) {
            UIImage *image = [UIImage imageWithData:entry.photo.lowResImageData];
            if (!image) image = [UIImage imageNamed:@"bills_moduleIcon.png"];
            entry.image = [image scaleToSize:cell.rightImageView.frame.size];

            [[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
                DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
                cell.rightImageView.image = entry.image;                
            }];
        }];
    }

向 UIImage 添加新的类别方法:

-(UIImage*)scaleToSize:(CGSize)size
{
    UIGraphicsBeginImageContext(size);
    [self drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return scaledImage;
}

滚动现在非常流畅。太棒了

最佳答案

几个问题:

  1. 我想看看这两条线花费了多少时间:

    Entry *entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
    UIImage *entryImage = entry.image;
    

    虽然我了解到 entry.image 是一个缓存图像,但我不太确定 objectAtIndexPath:indexPath 实际上没有检索 NSData 来自 Core Data(这是将图像存储在本地数据库而不是文件系统中的性能问题的很大一部分)。我倾向于在后台队列中执行此操作。

  2. 我不明白您为什么要向 oQueue 添加两个操作(可以同时操作!)。它应该如下所示,因此在检索图像之前不要尝试分派(dispatch)到主队列:

    [oQueue addOperationWithBlock:^(void) {
        UIImage *image = [UIImage imageWithData:entry.photo.lowResImageData];
        if (!image) image = [UIImage imageNamed:@"defaultImage.png"];
        entry.image = image;
    
        [[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
            DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
            cell.rightImageView.image = entry.image;
        }];
    }];
    
  3. 这可能并不重要,但您真的不应该为每个单元格创建一个新的 NSOperationQueue。您应该定义一个 NSOperationQueue 类属性,在 viewDidLoad 中对其进行初始化,然后在 cellForRowAtIndexPath 中使用该队列。我敢肯定,这并不重要,但不值得为表格的每一行创建一个队列。 (如果您曾经使用过基于服务器的图像检索,使用单个队列非常重要,因此您可以控制 maxConcurrentOperationCount。)

关于ios - 从 NSData 加载 UIImage 导致不稳定的 UITableView 滚动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14552817/

相关文章:

ios - 再次安装 cocoapods 后,如何保留我在 Pod 依赖项中所做的更改?

ios - 在cocos2d中用颜色填充一个圆圈

objective-c - 图像上的 UILabel

ios - 如果您有 dSYM,是否可以对 .ipa 文件进行逆向工程?

xcode - 调试时,我可以让 Xcode 忽略某些信号吗?

ios - 在 iOS6 中使用委托(delegate),我将如何在概念上设计一个允许我添加和编辑任务的待办事项列表委托(delegate)?

objective-c - 根据 Apple 指南,将文件保存到哪个目录?

iphone - 如何查看 In App Purchase Auto Renewable Subscription 是否有效

objective-c - Xcode 链接器错误 : file too small for architecture x86_64

ios - 修复 Swift Playground 中的低 FPS