Вопрос по asynchronous, ios5, objective-c, ios4, ios – Изображения, загруженные асинхронно, появляются в 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.

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

THX @ CarlVeazey - я могуХотя, похоже, синтаксис правильный - у вас есть пример? pepe
Не совсем уверен с AFNetworking, но выВозможно, придется реализовать обратный вызов успеха от ихUIImageView категория и убедитесь, что клетка получаетsetNeedsLayout вызвал его после обновления изображения. Вас также'Я должен убедиться, что повторное использование клетки неЭто мешает, что может быть сложно, но я думаю, это отдельная проблема. Carl Veazey
@carlveazy Есть ли способ обойти эту проблему изменения размера с помощью AFNetworking? pepe
Да я'Я думаю, что тыбуду звонитьsetImageWithURLRequest:placeholderImage:success:failure: и в блоке успеха убедитесь, что клетка получаетsetNeedsLayout называется. Carl Veazey

Ваш Ответ

5   ответов
5

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

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

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

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

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

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

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

это мое решение, используя категорию для UIImageView.

ПРИМЕЧАНИЕ: поскольку мы выполняем 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];} I ' L_Sonic
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];
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;
}

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

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

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