使用 UIImage 的 UICollectionView 滚动性能不佳 [英] Poor UICollectionView Scrolling Performance With UIImage

查看:16
本文介绍了使用 UIImage 的 UICollectionView 滚动性能不佳的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用中有一个 UICollectionView,每个单元格都是一个 UIImageView 和一些文本标签.问题是当我让 UIImageViews 显示他们的图像时,滚动性能很糟糕.它远没有 UITableView 甚至没有 UIImageView 的 UICollectionView 的滚动体验那么流畅.

I have a UICollectionView in my app, and each cell is a UIImageView and some text labels. The problem is that when I have the UIImageViews displaying their images, the scrolling performance is terrible. It's nowhere near as smooth as the scrolling experience of a UITableView or even the same UICollectionView without the UIImageView.

我发现 this question,似乎找到了答案,但它是用RubyMotion编写的,我不明白.我试图看看如何将它转换为 Xcode,但由于我也从未使用过 NSCache,所以有点难.那里的海报还指向这里 关于在他们的解决方案之外实现一些东西,但我也不知道该把代码放在哪里.可能是因为我不理解 第一个问题.

I found this question from a few months ago, and it seems like an answer was found, but it's written in RubyMotion, and I don't understand that. I tried to see how to convert it to Xcode, but since I have never used NSCache either, it's a little hard to. The poster there also pointed to here about implementing something in addition to their solution, but I'm not sure where to put that code either. Possibly because I don't understand the code from the first question.

有人能帮忙把它翻译成 Xcode 吗?

Would someone be able to help translate this into Xcode?

def viewDidLoad
  ...
  @images_cache = NSCache.alloc.init
  @image_loading_queue = NSOperationQueue.alloc.init
  @image_loading_queue.maxConcurrentOperationCount = 3
  ...
end

def collectionView(collection_view, cellForItemAtIndexPath: index_path)
  cell = collection_view.dequeueReusableCellWithReuseIdentifier(CELL_IDENTIFIER, forIndexPath: index_path)
  image_path = @image_paths[index_path.row]

  if cached_image = @images_cache.objectForKey(image_path)
    cell.image = cached_image
  else
    @operation = NSBlockOperation.blockOperationWithBlock lambda {
      @image = UIImage.imageWithContentsOfFile(image_path)
      Dispatch::Queue.main.async do
        return unless collectionView.indexPathsForVisibleItems.containsObject(index_path)
        @images_cache.setObject(@image, forKey: image_path)
        cell = collectionView.cellForItemAtIndexPath(index_path)
        cell.image = @image
      end
    }
    @image_loading_queue.addOperation(@operation)
  end
end

这是 第二个问题 第一个问题 表示解决了问题:

Here is the code from the second question that the asker of the first question said solved the problem:

UIImage *productImage = [[UIImage alloc] initWithContentsOfFile:path];

CGSize imageSize = productImage.size;
UIGraphicsBeginImageContext(imageSize);
[productImage drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
productImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

同样,我不确定如何/在哪里实施.

Again, I'm not sure how/where to implement that.

非常感谢.

推荐答案

这是我遵循的模式.始终加载异步并缓存结果.当异步加载完成时,不要假设视图的状态.我有一个简化负载的类,如下所示:

Here's the pattern I follow. Always load asynch and cache the result. Make no assumption about the state of the view when the asynch load finishes. I have a class that simplifies the loads as follows:

//
//  ImageRequest.h

// This class keeps track of in-flight instances, creating only one NSURLConnection for
// multiple matching requests (requests with matching URLs).  It also uses NSCache to cache
// retrieved images.  Set the cache count limit with the macro in this file.

#define kIMAGE_REQUEST_CACHE_LIMIT  100
typedef void (^CompletionBlock) (UIImage *, NSError *);

@interface ImageRequest : NSMutableURLRequest

- (UIImage *)cachedResult;
- (void)startWithCompletion:(CompletionBlock)completion;

@end

//
//  ImageRequest.m

#import "ImageRequest.h"

NSMutableDictionary *_inflight;
NSCache *_imageCache;

@implementation ImageRequest

- (NSMutableDictionary *)inflight {

    if (!_inflight) {
        _inflight = [NSMutableDictionary dictionary];
    }
    return _inflight;
}

- (NSCache *)imageCache {

    if (!_imageCache) {
        _imageCache = [[NSCache alloc] init];
        _imageCache.countLimit = kIMAGE_REQUEST_CACHE_LIMIT;
    }
    return _imageCache;
}

- (UIImage *)cachedResult {

    return [self.imageCache objectForKey:self];
}

- (void)startWithCompletion:(CompletionBlock)completion {

    UIImage *image = [self cachedResult];
    if (image) return completion(image, nil);

    NSMutableArray *inflightCompletionBlocks = [self.inflight objectForKey:self];
    if (inflightCompletionBlocks) {
        // a matching request is in flight, keep the completion block to run when we're finished
        [inflightCompletionBlocks addObject:completion];
    } else {
        [self.inflight setObject:[NSMutableArray arrayWithObject:completion] forKey:self];

        [NSURLConnection sendAsynchronousRequest:self queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
            if (!error) {
                // build an image, cache the result and run completion blocks for this request
                UIImage *image = [UIImage imageWithData:data];
                [self.imageCache setObject:image forKey:self];

                id value = [self.inflight objectForKey:self];
                [self.inflight removeObjectForKey:self];

                for (CompletionBlock block in (NSMutableArray *)value) {
                    block(image, nil);
                }
            } else {
                [self.inflight removeObjectForKey:self];
                completion(nil, error);
            }
        }];
    }
}

@end

现在单元格(集合或表格)更新相当简单:

Now the cell (collection or table) update is fairly simple:

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];

    NSURL *url = [NSURL URLWithString:@"http:// some url from your model"];
    // note that this can be a web url or file url

    ImageRequest *request = [[ImageRequest alloc] initWithURL:url];

    UIImage *image = [request cachedResult];
    if (image) {
        UIImageView *imageView = (UIImageView *)[cell viewWithTag:127];
        imageView.image = image;
    } else {
        [request startWithCompletion:^(UIImage *image, NSError *error) {
            if (image && [[collectionView indexPathsForVisibleItems] containsObject:indexPath]) {
                [collectionView reloadItemsAtIndexPaths:@[indexPath]];
            }
        }];
    }
    return cell;
}

这篇关于使用 UIImage 的 UICollectionView 滚动性能不佳的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆