异步下载的图像仅在点击或滚动后出现在UITableView中

pep*_*epe 12 asynchronous objective-c ios4 ios ios5

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

我遇到的问题是,如果我点击单元格或者向下滚动,图像才会出现.

当我点击单元格时,图像显示在左侧,将标题和副标题推向右侧.

向下滚动时,图像会显示在细胞显示的位置.

这是我的代码(我正在使用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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

dan*_*anh 12

混合asynch和table时要注意的事情是异步在未来的未知时间结束,可能是在单元格被滚动,删除,重用等之后.

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

// ...
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"];
}
// ...
Run Code Online (Sandbox Code Playgroud)

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

- (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
            }
        }
    }];
}
Run Code Online (Sandbox Code Playgroud)

为此,应将帖子创建为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]];
Run Code Online (Sandbox Code Playgroud)

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

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

// lazy init on the getter...

- (NSMutableDictionary *)imageCache {
    if (!_imageCache) {
        _imageCache = [NSMutableDictionary dictionary];
    }
    return _imageCache;
}
Run Code Online (Sandbox Code Playgroud)

现在,在配置单元格时,通过检查缓存来查看是否存在缓存图像...

// change to the cellForRowAtIndexPath method
NSString *postpictureUrl = [post objectForKey:@"picture"];
UIImage  *postImage = [self.imageCache valueForKey:postpictureUrl];
Run Code Online (Sandbox Code Playgroud)

下载图像后,将其缓存...

// change to the asynchLoad: method I suggested
UIImage *image = [UIImage imageWithData:data];
[self.imageCache setValue:image forKey:urlString];
Run Code Online (Sandbox Code Playgroud)


pep*_*epe 5

通过在此行中放置占位符来解决此问题

...
[cell.imageView setImageWithURL:[NSURL URLWithString:postpictureUrl] placeholderImage:[UIImage imageNamed:@"default"]];
....
Run Code Online (Sandbox Code Playgroud)

占位符需要具有与缩略图类似的尺寸比率以避免失真.

  • 具体来说,原因是如果`UITableViewCell`的`imageView`属性为nil,则在`layoutSubviews`期间它会将`imageView`调整为{0,0}.设置占位符可防止此调整大小.如果你不想拥有占位符,你必须在设置`cell.imageView.image`时调用`setNeedsLayout`.编辑:danh提出了一个非常好的观点,你可以重新加载单元格来强制正确的大小. (2认同)