UITableViewCell загружает изображения и повторно используемые ячейки - PullRequest
6 голосов
/ 08 февраля 2012

Мне нужно загрузить из Интернета / файлов некоторые UIImages. Я искал и нашел в другом вопрос этот код:

    if (![[NSFileManager defaultManager] fileExistsAtPath:user.image]) {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,  0ul);
        dispatch_async(queue, ^{
            NSData *imageData =[NSData dataWithContentsOfURL:[NSURL URLWithString:user.imageURL]];

            [imageData writeToFile:user.image atomically:YES];
            dispatch_sync(dispatch_get_main_queue(), ^{
                UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
                UIImage *image = [UIImage imageWithData:imageData];
                [self.imageFriends setObject:image forKey:[NSNumber numberWithInt:user.userId]];
                cell.imageView.image = image;
                [cell setNeedsLayout];
                NSLog(@"Download %@",user.image);
            });
        });
        cell.imageView.image=[UIImage imageNamed:@"xger86x.jpg"];
    } else {
        NSLog(@"cache");
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,  0ul);
        dispatch_async(queue, ^{
            UIImage *image = [UIImage imageWithContentsOfFile:user.image];
            //[self.imageFriends setObject:image forKey:[NSNumber numberWithInt:user.userId]];
            dispatch_sync(dispatch_get_main_queue(), ^{
                UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
                newCell.imageView.image=image;
                [newCell setNeedsLayout];
            });
        });
    }

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

Итак, вопрос в том ... как я могу загрузить UIImages в правильную ячейку, когда использую очереди для их извлечения? Спасибо!

Ответы [ 2 ]

7 голосов
/ 08 февраля 2012

Я подозреваю, что неправильные изображения, которые вы видите, являются следствием того, что вы не установили изображение заполнителя в случае, если у вас есть локальная копия изображения, но при этом вы все же получаете локальную копию асинхронно.Также в коде, который вы добавили для загрузки локальной копии, вы используете UIImage a UIKit компонент в фоновом потоке.

Также интересно, что вы, кажется, делаете какое-то UIImage кэширование.Добавление изображений к тому, что я предполагаю, является свойством NSMutableArray с именем imageFriends.Но вы, кажется, закомментировали добавление кеша, если у вас есть локальная копия файла.Кроме того, в вашем опубликованном коде никогда не используется кэшированный UIImages.

Хотя 2 уровня кэширования кажутся немного излишними, если вы хотите это сделать, вы можете сделать что-то вроде этого:

UIImage *userImage = [self.imageFriends objectForKey:[NSNumber numberWithInt:user.userId]];
if (userImage) { // if the dictionary of images has it just display it
    cell.imageView.image = userImage;
}
else {
    cell.imageView.image = [UIImage imageNamed:@"xger86x.jpg"]; // set placeholder image
    BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:user.image];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData *imageData = nil;
        if (fileExists){
            imageData = [NSData dataWithContentsOfFile:user.image];
        }
        else {
            imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:user.imageURL]];
            [imageData writeToFile:user.image atomically:YES];
        }
        if (imageData){
           dispatch_async(dispatch_get_main_queue(), ^{ 
                    // UIKit, which includes UIImage warns about not being thread safe
                    // So we switch to main thread to instantiate image
               UIImage *image = [UIImage imageWithData:imageData];
               [self.imageFriends setObject:image forKey:[NSNumber numberWithInt:user.userId]];
               UITableViewCell *lookedUpCell = [tableView cellForRowAtIndexPath:indexPath];
               if (lookedUpCell){
                   lookedUpCell.imageView.image = image;
                   [lookedUpCell setNeedsLayout];
               }
           }); 
        }
    });
}

UIImage являются частью UIKit и не безопасны для потоков.Но вы можете загрузить NSData в другой поток.

6 голосов
/ 08 февраля 2012

Вы загружаете изображение асинхронно, поэтому во время быстрой прокрутки ячейки используются повторно быстрее, чем загружаются изображения.Один из способов избежать загрузки неправильного изображения - проверить, не использовалась ли уже ячейка при загрузке изображения.Или отмените все выполняющиеся запросы при удалении из очереди новых ячеек.

Я бы также рекомендовал посмотреть AFNetworking , поскольку в нем содержится полезная категория для UIImageView, так что вы можете сделать что-то вроде этого:

[imageView setImageWithURL:[NSURL URLWithString:@"http://i.imgur.com/r4uwx.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder-avatar"]];

Также содержит метод cancelImageRequestOperation для отмены текущих запросов.Тогда ваш код будет выглядеть так:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
} else {
    [cell.imageView cancelImageRequestOperation];
}
[cell.imageView setImageWithURL:[NSURL URLWithString:user.imageURL] placeholderImage:[UIImage imageNamed:@"xger86x.jpg"]]; 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...