在从NSData加载图像时,我正在让我的tableview顺畅滚动。
一些背景......我将图像NSData存储在核心数据中(使用'允许外部存储'选项,所以我不认为存储URL或类似的东西就是这里的解决方案)。此外,我存储两种类型的数据,一种是高分辨率的UIImagePNGRepresentation,另一种是低分辨率的UIImageJPGRepresentation。我正在为我的桌面视图加载低分辨率图像。条目对象上的图像属性不存储在核心数据中,事后其设置。我是这样做的:
- (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);
}
我认为我的下一个攻击计划是乱用存储的图像大小(如评论中所述),或者在表格滚动时尝试不更新imageViews。对这两种方法的任何想法都非常感谢!我做完那件事后再检查
更新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;
}
滚动现在非常流畅。真棒
答案 0 :(得分:2)
有几个问题:
我想知道这两条线花了多少时间:
Entry *entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
UIImage *entryImage = entry.image;
虽然我认为entry.image
是一张缓存的图片,但我并不相信objectAtIndexPath:indexPath
实际上并没有从核心数据中检索NSData
(这是一个很好的部分)将图像存储在本地数据库而不是文件系统中的性能问题。我倾向于在后台队列中这样做。
我不明白为什么要向oQueue
添加两个操作(可以同时操作!)。它应该如下所示,因此在检索图像之前不要尝试调度到主队列:
[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;
}];
}];
这可能不重要,但你真的不应该为每个单元格创建一个新的NSOperationQueue
。您应该定义一个NSOperationQueue
的类属性,在viewDidLoad
中对其进行初始化,然后在cellForRowAtIndexPath
中使用该队列。我确定这不重要,但是为表格的每一行创建一个队列的开销并不值得。 (如果你去过基于服务器的图像检索,使用单个队列非常重要,这样你就可以控制maxConcurrentOperationCount
。)
答案 1 :(得分:0)
UIImage在需要绘制图像之前不会实际进行解码,因此当您将图像分配给图像视图时,它实际上会发生在主线程上。
我有一个UIImage类别,它会强制解码图像(你应该在后台线程上运行):https://github.com/tonymillion/TMDiskCache