异步下载的图像仅在点击或滚动后出现在 UITableView 中 [英] Images downloaded asynchronously only appear in UITableView after tap or scroll

查看:20
本文介绍了异步下载的图像仅在点击或滚动后出现在 UITableView 中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我成功地将博客文章中的缩略图异步加载到我的 UITableView 中.

I am successfully loading thumbnail images from blog posts asynchronously into my UITableView.

我遇到的问题是图像只有在我点击单元格或向下滚动时才会出现.

The issue I'm having is that the images only appear if I tap the cell OR if I scroll down.

当我点击单元格时,图像出现在左侧,将标题和副标题推到右侧.

When I tap the cell, the image appears on the left, pushing the Title and Subtitle to the right.

当我向下滚动时,图像会显示在单元格中应显示的位置.

When I scroll down, the images appear where they should in the cells as they are revealed.

这是我的代码(我正在使用 AFNetworking):

Here's my code (I'm using AFNetworking):

#import "UIImageView+AFNetworking.h"

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return posts.count;
}

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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

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

    NSDictionary *post           = [posts objectAtIndex:indexPath.row];
    NSString     *postpictureUrl = [post objectForKey:@"picture"];

    [cell.imageView setImageWithURL:[NSURL URLWithString:postpictureUrl]];

    cell.textLabel.text       = [post objectForKey:@"post_text"];
    cell.detailTextLabel.text = [post objectForKey:@"post_author_name"];
    return cell;
}

我在 iPhone 6.0 模拟器、XCode 4.5、OSX MtLion 中看到了这一点.

I am seeing this in the iPhone 6.0 simulator, XCode 4.5, OSX MtLion.

任何想法为什么图像没有绘制在初始屏幕上?

Any ideas why the images are not drawn on the initial screen?

推荐答案

在混合 asynch 和 table 时要注意的是 asynch 在未来未知的时间结束,可能是在单元格滚动离开之后、删除、重复使用等.

The thing you want to be aware of when mixing asynch and tables is that the asynch finishes at an unknown time in the future, possibly after the cell is scrolled away, removed, reused, etc.

此外,如果该单元格被滚动离开,则从网络中提取的图像会丢失.不确定 AFNetworking 是否为您缓存,但最好不要假设.这是使用本地网络的解决方案:

Also, the image that gets pulled from the web is lost if that cell is scrolled away. Not sure if AFNetworking caches for you, but it might be better to not assume. Here's a solution using native networking:

// ...
NSDictionary *post           = [posts objectAtIndex:indexPath.row];
NSString     *postpictureUrl = [post objectForKey:@"picture"];

// find a place in your model, or add one, to cache an actual downloaded image
UIImage      *postImage      = [post objectForKey:@"picture_image"];

if (postImage) {
    cell.imageView.image = postImage;   // this is the best scenario: cached image
} else {
    // notice how we don't pass the cell - we don't trust its value past this turn of the run loop
    [self asynchLoad:postpictureUrl forIndexPath:indexPath];
    cell.imageView.image = [UIImage imageNamed:@"default"];
}
// ...

现在,没有任何第三方帮助的严肃异步加载

Now, a no-nonsense asynch load without any 3rd party help

- (void)asynchLoad:(NSString *)urlString forIndexPath:(NSIndexPath *)indexPath {

    NSURL *url = [NSURL urlWithString:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        if (!error) {

            // create the image
            UIImage *image = [UIImage imageWithData:data];

            // cache the image
            NSDictionary *post = [posts objectAtIndex:indexPath.row];
            [post setObject:image forKey:@"picture_image"];

            // important part - we make no assumption about the state of the table at this point
            // find out if our original index path is visible, then update it, taking 
            // advantage of the cached image (and a bonus option row animation)

            NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows];
            if ([visiblePaths containsObject:indexPath]) {
                NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
                [self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation: UITableViewRowAnimationFade];
                // because we cached the image, cellForRow... will see it and run fast
            }
        }
    }];
}

为此,帖子应创建为 NSMutableDictionary...

For this to work, the posts should be created as NSMutableDictionary...

// someplace in your code you add a post to the posts array.  do this instead.

NSDictionary *postData = // however you get a new post
[posts addObject:[NSMutableDictionary dictionaryWithDictionary:postData]];

或者,如果很难直接更改帖子模型,您可以设置另一个结构来缓存下载的图像.由 url 字符串键入的可变字典是一个很好的结构:

Alternatively, if it's hard to change the posts model directly, you can setup another structure to cache the downloaded images. A mutable dictionary keyed by the url strings is a good structure to use:

@property (nonatomic,strong) NSMutableDictionary *imageCache;
@synthesize imageCache=_imageCache;

// lazy init on the getter...

- (NSMutableDictionary *)imageCache {
    if (!_imageCache) {
        _imageCache = [NSMutableDictionary dictionary];
    }
    return _imageCache;
}

现在,在配置单元格的时候,通过查看缓存来查看是否有缓存的图片...

Now, when configuring the cell, see if there's a cached image by checking the cache...

// change to the cellForRowAtIndexPath method
NSString *postpictureUrl = [post objectForKey:@"picture"];
UIImage  *postImage = [self.imageCache valueForKey:postpictureUrl];

一旦下载了图片,缓存它...

And once an image is downloaded, cache it...

// change to the asynchLoad: method I suggested
UIImage *image = [UIImage imageWithData:data];
[self.imageCache setValue:image forKey:urlString];

这篇关于异步下载的图像仅在点击或滚动后出现在 UITableView 中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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