异步地将图像加载到UICollectionView中?

8
如何异步加载图像到 UICollectionview?在下面的方法中实现?
- (PSTCollectionViewCell *)collectionView:(PSTCollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

 bookImage = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", docPath, [[preview objectAtIndex:indexPath.row] lastPathComponent]]];
 [[cell grid_image] setImage:bookImage];

}

viewdidload() 中,我使用以下异步调用将图像加载到“预览”NSMutablearray中。
 dispatch_queue_t imageLoadQueue = dispatch_queue_create("com.GMM.assamkar", NULL);

dispatch_async(imageLoadQueue, ^{
    //Wait for 5 seconds...
    usleep(1000000);
    docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];


    for(int k=0; k <[allImage count] ;k++){


        imgURL = [allImage objectAtIndex:k];
        [imagePreview addObject:imgURL];
        imgData=[NSData dataWithContentsOfURL:[NSURL URLWithString:imgURL]];


        [imgData writeToFile:[NSString stringWithFormat:@"%@/%@", docPath, [allImage lastPathComponent]] atomically:YES];

    }

    [[self collectionView] reloadData];


});

请帮我一下,现在加载需要的时间太长了...


具体是什么导致了这么长的时间?图片的尺寸是多少? - Wain
你应该在主线程中使用以下代码:[[self collectionView] reloadData]; 这不是答案,只是一条注释。 - stosha
使用EGOImageView: https://github.com/enormego/EGOImageLoading - Gaurav Patel
3个回答

9
尝试回答你的主要问题“如何异步加载图像到UICollectionview?” 我建议使用"Natasha Murashev"在这里所提供的解决方案,它对我很好用且简单。如果在allImage属性中,imgURL = [allImage objectAtIndex:k]; 保存了一组URL,则可以按如下方式更新您的collectionView:cellForItemAtIndexPath:方法:
- (PSTCollectionViewCell *)collectionView:(PSTCollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSURL *url = [NSURL URLWithString:[allImage objectAtIndex:indexPath]];

    [self downloadImageWithURL:url completionBlock:^(BOOL succeeded, NSData *data) {
        if (succeeded) {
            cell.grid_image.image = [[UIImage alloc] initWithData:data];
        }
    }];
}

请在您的类中添加downloadImageWithURL:completionBlock:方法,该方法将异步加载图片,并在成功下载图片后自动更新CollectionView(集合视图)中的单元格。

- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, NSData *data))completionBlock
{
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        if (!error) {
            completionBlock(YES, data);
        } else {
            completionBlock(NO, nil);
        }
    }];
}

我看到你试图在视图出现之前预加载图片,所以也许我的解决方案不是你想要的,但从你的问题来看很难说。无论如何,你也可以用这个方法实现你想要的效果。

Swift 2.2 Swift中的解决方案。

public typealias ImageFetchCompletionClosure = (image: UIImage?, error: NSError?, imageURL: NSURL?) -> Void

extension String {
   func fetchImage(completionHandler: (image: UIImage?, error: NSError?, imageURL: NSURL?) -> Void) {
        if let imageURL = NSURL(string: self) {
            NSURLSession.sharedSession().dataTaskWithURL(imageURL) { data, response, error in
                guard
                    let httpURLResponse = response as? NSHTTPURLResponse where httpURLResponse.statusCode == 200,
                    let mimeType = response?.MIMEType where mimeType.hasPrefix("image"),
                    let data = data where error == nil,
                    let image = UIImage(data: data)
                    else {
                        if error != nil {
                            completionHandler(image: nil, error: error, imageURL: imageURL)
                        }
                        return
                }
                dispatch_sync(dispatch_get_main_queue()) { () -> Void in
                    completionHandler(image: image, error: nil, imageURL: imageURL)
                }
            }.resume()
        }
    }
}

使用示例:

    "url_string".fetchImage { (image, error, imageURL) in
        // handle different results, either image was downloaded or error received
    }

1
难道不是像细胞可以重复使用,你可以高效地将细胞覆盖为其他图像数据吗?此外,你的示例中未定义细胞 ;) - pronebird
它主要是为了回答如何异步加载图像,而不是如何有效地处理单元格 :) 虽然,感谢您指出这一点。 - Jonauz

0

这段代码创建了串行队列:

dispatch_queue_t imageLoadQueue = dispatch_queue_create("com.GMM.assamkar", NULL);

因此,队列中的每个任务都在同一个线程连续执行。因此,您的单个工作线程通过为每个加载任务休眠来维持,这会减慢所有加载过程。

使用异步并发队列,可以使用类似以下内容的方法:

dispatch_queue_t imageLoadQueue = dispatch_queue_create("com.GMM.assamkar", DISPATCH_QUEUE_CONCURRENT);

所以在主线程上使用reloadData。像这样:dispatch_async(dispatch_get_main_queue(), ^{ //reloadData }); - user2595925
我认为我应该使用一种新的延迟加载方法来显示我的PSTCollectionView。 - Navi

-1

使用延迟加载文件来满足您的需求。

LazyLoad.h
LazyLoad.m

使用方法如下:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];

    [cell addSubview:[self addViewWithURL:@"http://openwalls.com/image/399/explosion_of_colors_1920x1200.jpg" NFrame:CGRectMake(0, 0, 50, 50)]];

    cell.backgroundColor=[UIColor blueColor];
    return cell;
}

-(UIView*)addViewWithURL:(NSString*)urlStr NFrame:(CGRect)rect
{
    LazyLoad *lazyLoading;

    lazyLoading = [[LazyLoad alloc] init];
    [lazyLoading setBackgroundColor:[UIColor grayColor]];
    [lazyLoading setFrame:rect];

    [lazyLoading loadImageFromURL:[NSURL URLWithString:urlStr]];
    return lazyLoading;
}

在此下载演示版本


如何在UICollectionview单元格中显示图像,这是我的问题。 - Navi
在CollectionView中,cell.imageview.image属性不存在,只存在于tableView中。我的代码将异步加载图像并将其作为子视图添加到collectionView单元格中。 - Prince Kumar Sharma
将myCode添加到https://dev59.com/8GMm5IYBdhLWcg3wHMJ0#17856406中,或者您需要使用自定义单元格。 - Prince Kumar Sharma
你的代码不起作用了,/H和.M文件的链接为空。 - Navi
LazyLoad类解决了我的初始问题,但也带来了一个新的问题。在视图完成滚动之前,图片不会加载。是否有一种方法可以让它们在滚动时进入屏幕就开始加载? - Roeliee
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接