UICollectionViewController продолжает перестраиваться из асинхронной загрузки изображения

#asynchronous #uicollectionview #uicollectionviewcell

#асинхронный #uicollectionview #uicollectionview ячейка #uicollectionviewcell

Вопрос:

В моем приложении. Я получаю кучу URL-адресов изображений в основном потоке следующим образом:

 - (void)viewDidLoad
{
    [super viewDidLoad];
    [self populateCollection];
}

- (void)populateCollection
{
    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:[MTGJSONBridge JSONURLWithSetCode:@"LEA"]]
                                                         options:kNilOptions
                                                           error:nil];
    NSLog(@"Json: %@", json);
    NSArray *cards = json[@"cards"];
    _URLs = [NSMutableArray arrayWithCapacity:cards.count];
    for (NSDictionary *card in cards)
    {
        NSURL *imageURL = [MTGJSONBridge URLWithSetCode:@"LEA" cardName:card[@"imageName"]];
        if (imageURL)
            [_URLs addObject:imageURL];
    }
}
  

Это дает мне около 300 URL-адресов за 0,2 секунды. Затем я пытаюсь асинхронно загружать изображения с каждого URL-адреса следующим образом:

 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"reuseIdentifier";
    MTGCardCollectionCell *cell = (MTGCardCollectionCell *)[collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];

    NSURL *url = _URLs[indexPath.row];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // No explicit autorelease pool needed here.
        // The code runs in background, not strangling
        // the main run loop.
        UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
        dispatch_sync(dispatch_get_main_queue(), ^{
            // This will be called on the main thread, so that
            // you can update the UI, for example.
            cell.imageView.image = image;
        });
    });
    return cell;
}
  

У меня также есть это:

 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 1;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return _URLs.count;
}
  

Коллекция загружается очень быстро и к тому же асинхронно (я могу прокручивать во время загрузки, и я вижу, как появляются новые изображения). Проблема в том, что при прокрутке вверх и вниз, даже после загрузки всех изображений, объект продолжает перестраиваться в случайном порядке: я смотрю на одну ячейку, а затем ее изображение переключается на другое без видимой причины. Почему это происходит?

Ответ №1:

Я обнаружил очень простое решение:

 if (cell.imageView.image == nil)
{
    NSURL *url = _URLs[indexPath.row];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // No explicit autorelease pool needed here.
        // The code runs in background, not strangling
        // the main run loop.
        UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
        dispatch_sync(dispatch_get_main_queue(), ^{
            // This will be called on the main thread, so that
            // you can update the UI, for example.
            cell.imageView.image = image;
        });
    });
}
else
{
    NSLog(@"Cell image isn't nil");
}
  

Все, что мне нужно сделать, это проверить, не загружена ли ячейка. Оказывается, - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath метод вызывается всякий раз, когда в поле зрения попадает ячейка. Даже если это было только в представлении.