Вопрос по ios, ios4, objective-c, ios5, asynchronous – Изображения, загруженные асинхронно, появляются в UITableView только после нажатия или прокрутки

12

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

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

Да, я думаю, ты позвонишьsetImageWithURLRequest:placeholderImage:success:failure: и в блоке успеха убедитесь, что клетка получаетsetNeedsLayout называется. Carl Veazey
@carlveazy Есть ли способ обойти эту проблему изменения размера с помощью AFNetworking? pepe
кажется, здесь есть вариант, но не уверен, что он соответствует тому, что вы предлагаете -afnetworking.github.com/AFNetworking/Categories/… pepe
Не совсем уверен с AFNetworking, но вам, вероятно, придется реализовать обратный вызов успеха из ихUIImageView категория и убедитесь, что клетка получаетsetNeedsLayout вызвал его после обновления изображения. Кроме того, вы должны быть уверены, что повторное использование ячеек здесь не мешает, что может быть непросто, но я полагаю, что это отдельная проблема. Carl Veazey
спасибо @CarlVeazey - я не могу понять синтаксис, хотя - у вас есть пример? pepe

Ваш Ответ

5   ответов
2

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

человек ты спас мои жалкие окурки. Спасибо!
1


ПРИМЕЧАНИЕ: поскольку мы выполняем self.image = nil в первой строке, вы должны установить изображение-заполнитель для cell.ImageView после вызова этого метода.

- (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
Было бы разумно удалить себя изнутри блока. Вместо этого используйте слабую ссылку на экземпляр imageviews. как то __weak UIImageView * wself = self; if (data ** wself.window) {wself.image = [UIImage imageWithData: data];}
0

Я довольно опаздываю на вечеринку, но если копнуть глубжеUIImageView + AFNetworking документы вы найдете способ– 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];
5

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

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

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

В частности, причина этого заключается в том, что еслиUITableViewCell& APOS; simageView свойство ноль, во времяlayoutSubviews это изменяет размерimageView до {0,0}. Установка заполнителя предотвращает это изменение размера. Если вы не хотите иметь заполнитель, вы должны позвонитьsetNeedsLayout в то время, которое вы установилиcell.imageView.image, РЕДАКТИРОВАТЬ: Данх поднимает действительно хороший момент, что вы можете перезагрузить саму ячейку, чтобы заставить правильный размер.
12

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

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

Теперь несложная асинхронная загрузка без посторонней помощи

- (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];
@ danh это звучит как хорошая идея - я получаю сообщение об ошибке разбора в этой строке[post setObject:image forKey:@"picture_image"]; --- No visible @interface for 'NSDictionary' declares the selector 'setObject:forKey:' pepe
Пост представляет собой словарь. Чтобы изменить это, оно должно быть изменчивым. Вы можете просто сделать их изменяемыми, когда создаете их, или изменять их по ходу работы. Я добавлю немного кода, чтобы проиллюстрировать это позже.
Не уверен, что слишком сложно сделать вашу модель изменчивой. Я подправил ответ, чтобы проиллюстрировать параллельную структуру данных для кэширования изображений.
Это действительно хороший ответ. Делать это на основе индекса является абсолютно правильным способом. Кроме того, спасибо за предложение перезагрузить строки - это на самом деле имеет больше смысла, чемsetNeedsLayout и я думаю, что я буду использовать это теперь! Я не пробовал это, но вариант может включать использованиеNSCache с индексными путями в качестве самих ключей.

Похожие вопросы