使用多线程下载UITableViewCell的图像,但在滚动UITableView之前无法加载图像

时间:2014-09-30 07:41:40

标签: ios objective-c multithreading uitableview

我正在尝试使用多线程在UITableViewCell中下载图像。但是,当我使用模拟器查看结果时,在滚动表视图之前无法加载图像。

我在StackOverFlow中观看了很多示例和教程,但它仍然无法正常工作。实际上,我从Flickr服务器下载图像并将它们存储到缓存的字典中。但我仍然可以第一次加载图像,除非我滚动表格视图并且图像开始出现。

这是我的代码:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];

    NSString *cellIdentifier = [NSString stringWithFormat:@"cell%ld", (long)indexPath.row];

    NSDictionary *photo = self.recentPhotos [indexPath.row];
    NSString *title = [photo valueForKeyPath:FLICKR_PHOTO_TITLE];

    cell.textLabel.text = title;
    [cell.textLabel setFont:[UIFont fontWithName:@"OpenSans" size:18]];

    if ([self.cashedImages objectForKey:cellIdentifier] != nil) {
        cell.imageView.image = [self.cashedImages objectForKey:cellIdentifier];
    }
    else
    {
        dispatch_queue_t queue = dispatch_queue_create("fetch photos", 0);
        dispatch_async(queue, ^{

            NSURL *imageURL = [FlickrFetcher URLforPhoto:photo format:FlickrPhotoFormatSquare];
            UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageURL]];

            dispatch_async(dispatch_get_main_queue(), ^{
                if ([tableView indexPathForCell:cell].row == indexPath.row) {
                    [self.cashedImages setValue:image forKey:cellIdentifier];
                    cell.imageView.image = [self.cashedImages valueForKey:cellIdentifier];
                }
            });
        });
    }
    return cell;
}

5 个答案:

答案 0 :(得分:1)

对于此任务,最好使用库。通常我使用的SDWebImage非常易于使用,并且还可以处理图像兑现。

https://github.com/rs/SDWebImage

一个例子可能是:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *MyIdentifier = @"MyIdentifier";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];

    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                   reuseIdentifier:MyIdentifier] ;
    }

    // Here we use the new provided setImageWithURL: method to load the web image
    [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
               placeholderImage:[UIImage imageNamed:@"placeholder.png"]];

    cell.textLabel.text = @"My Text";
    return cell;
}

编辑:

如果要先下载所有图像并缓存它们,则不应在tableviewCell中处理此图像。 TableViewCells将在创建时开始下载 - >当它们变得可见时。

SDWebImageView兑现之前下载的图像,因此尝试在viewDidLoad方法中添加此代码:

for (NSURL* url in self.recentPhotos) {
    SDWebImageManager *manager = [SDWebImageManager sharedManager];

    [manager downloadImageWithURL:url options:SDWebImageContinueInBackground progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
        //Your code if needed
    }];
}

SDWebImage根据图像的绝对URL路径缓存图像。

答案 1 :(得分:1)

注意! GDImageLoader now(2016)拥有完整,超棒的Swift版本。它几乎每天都在维护 - 它可能是iOS中最重要且最基本的完美库。直到Apple明智地包含缓存,它基本上都是必须使用的库。


GDImageLoader非常简单,坚固 - 它是最好的..

https://github.com/AndreyLunevich/DLImageLoader-iOS/tree/master/DLImageLoader

没有手动,没有学习曲线 - 一个命令。完全太棒了。

-(UICollectionViewCell *)collectionView:(UICollectionView *)cv
          cellForItemAtIndexPath:(NSIndexPath *)indexPath
    {
    NSInteger thisRow = indexPath.row;

    MeetingsCell *cell;
    cell = [cv dequeueReusableCellWithReuseIdentifier:
        @"CellPlayersB2" forIndexPath:indexPath];

    cell.layer.shouldRasterize = YES;   // TYPICALLY NEEDED ON iPhone6+
    cell.layer.rasterizationScale = [UIScreen mainScreen].scale;

    NSDictionary *mtg = CLOUD.players[thisRow];

    NSString *hostText = mtg[@"host"]; etc...
    cell.whenDescrip.text = etc ...;

    NSString *imUrl = mtg[@"image"];

    __weak UIBookView *loadBooky = cell.booky;
    [DLImageLoader loadImageFromURL:imUrl
      completed:^(NSError *error, NSData *imgData)
        {
        if (loadBooky == nil) return;
        [loadBooky use:[UIImage imageWithData:imgData]];
        }];

    return cell;
    }

答案 2 :(得分:0)

表格可能不会重新绘制内容。一个修复可能是将图像保存在self.cashedImaged(你知道这是一个错字,对吧;)),然后指示UITableView使用reloadRowsAtIndexPaths重新加载单个单元格:

答案 3 :(得分:0)

我认为最好在UIImageView上添加一个类别,并添加一个链接此方法的方法:

@interface UIImageView (Caching)
- (void)setImageWithFlickrURL:(NSURL *)url;
@end

并将您的缓存逻辑放在那里。 然后在你的cellForRow:indexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //...
    NSURL *imageURL = [FlickrFetcher URLforPhoto:photo format:FlickrPhotoFormatSquare];
    [cell.imageView setImageWithFlickrURL:imageURL];

    return cell;
}

或者,您可以使用提供AFNetworking UIImageView+AFNetworking

的缓存机制

- (void)setImageWithURL:(NSURL *)url;

答案 4 :(得分:0)

您需要添加一行:

cell.imageView.image = [self.cashedImages valueForKey:cellIdentifier];

//add this line to make tableview redraw
[cell setNeedsLayout];