Изображения, загруженные асинхронно, появляются только в UITableView после нажатия или прокрутки

Я успешно загружаю миниатюры из сообщений блога асинхронно в мой 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;
}

Я вижу это в симуляторе iPhone 6.0, XCode 4.5, OSX MtLion.

есть идеи, почему изображения не рисуются на начальном экране?

5 ответов


вещь, которую вы хотите знать при смешивании asynch и таблиц, заключается в том, что asynch заканчивается в неизвестное время в будущем, возможно, после того, как ячейка прокручивается, удаляется, повторно используется и т. д.

кроме того, изображение, которое извлекается из интернета, теряется, если эта ячейка прокручивается. Не уверен, что 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"];
}
// ...

теперь, не-нонсенс asynch нагрузки без каких-либо 3-е лица

- (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...

// 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, является хорошей структурой для использования:

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

// lazy init on the getter...

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

теперь, при настройке ячейки, посмотрите, есть ли кэшированное изображение, проверив кэш...

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

и после изображение загружается, кэшируется...

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

проблема решена путем размещения заполнителя в этой строке

...
[cell.imageView setImageWithURL:[NSURL URLWithString:postpictureUrl] placeholderImage:[UIImage imageNamed:@"default"]];
....

заполнитель должен иметь отношение размеров, подобное миниатюре, чтобы избежать искажений.


Я долго чесал голову и, наконец, понял это.

моя ошибка заключалась в том, что я устанавливал изображение в cell.imageView когда я должен устанавливать свою фактическую розетку cell.eventImageView. Он возился с общим imageview, предоставленным в UITableViewCell. Надеюсь, это кому-то поможет.


Это мое решение, используя категорию для UIImageView.
Примечание: поскольку мы выполняем себя.image = nil в первой строке необходимо задать изображение-заполнитель для ячейки.Книги после вызова этого метода.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
   [cell.imageView loadImageForURLString:row.imageUrl];
   cell.imageView.image = tempImage;
...
}

категории:

#import "UIImageView+AsyncLoad.h"

@implementation UIImageView (AsyncLoad)

- (void)loadImageForURLString:(NSString *)imageUrl
{
    self.image = nil;

    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageUrl]];
    [NSURLConnection sendAsynchronousRequest:request
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse * response, NSData * data, NSError * error)
     {
         [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
         if (data && self.window) {
             self.image = [UIImage imageWithData:data];
         }
     }];
}

@end

Я очень опоздала на вечеринку, но если вы копать немного глубже в UIImageView+AFNetworking docs вы найдете способ – setImageWithURLRequest:placeholderImage:success:failure: который вы можете использовать для перезагрузки ячейки, когда изображение доступно:

NSURLRequest *urlRequest = [NSURLRequest requestWithURL: [NSURL URLWithString: imageURL]];
__weak UITableViewCell *weakCell = cell;

[cell.imageView setImageWithURLRequest: urlRequest
                      placeholderImage: nil
                               success: ^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {

                                   __strong UITableViewCell *strongCell = weakCell;
                                   strongCell.imageView.image = image;

                                   [tableView reloadRowsAtIndexPaths: @[indexPath]
                                                    withRowAnimation: UITableViewRowAnimationNone];

                               } failure: NULL];