Ответ 1
Сессия 211 - Создание параллельных пользовательских интерфейсов на iOS, начиная с WWDC 2012, обсуждает проблему изменения изображения ячейки, поскольку фоновые задачи догоняют (начиная с 38 м15 с).
Вот как вы решаете эту проблему. После того, как вы загрузили изображение в фоновом режиме, используйте путь индекса, чтобы найти текущую ячейку, если она есть, которая отображает элемент, содержащий это изображение. Если вы получите ячейку, установите изображение ячейки. Если вы получаете нуль, в данный момент ячейка не отображает этот элемент, поэтому просто удаляйте изображение.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
cell.imageView.image = nil;
dispatch_async(myQueue, ^{
[self bg_loadImageForRowAtIndexPath:indexPath];
});
return cell;
}
- (void)bg_loadImageForRowAtIndexPath:(NSIndexPath *)indexPath {
// I assume you have a method that returns the path of the image file. That
// method must be safe to run on the background queue.
NSString *path = [self bg_pathOfImageForItemAtIndexPath:indexPath];
UIImage *image = [UIImage imageWithContentsOfFile:path];
// Make sure the image actually loads its data now.
[image CGImage];
dispatch_async(dispatch_get_main_queue(), ^{
[self setImage:image forItemAtIndexPath:indexPath];
});
}
- (void)setImage:(UIImage *)image forItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = (MyCell *)[collectionView cellForItemAtIndexPath:indexPath];
// If cell is nil, the following line has no effect.
cell.imageView.image = image;
}
Вот простой способ уменьшить "укладку" фоновых задач, загружая изображения, которые больше не нужны виду. Дайте себе переменную экземпляра NSMutableSet
:
@implementation MyViewController {
dispatch_queue_t myQueue;
NSMutableSet *indexPathsNeedingImages;
}
Когда вам нужно загрузить изображение, поместите путь указателя к ячейке в наборе и отправьте задачу, которая выведет один указательный путь из набора и загрузит его изображение:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
cell.imageView.image = nil;
[self addIndexPathToImageLoadingQueue:indexPath];
return cell;
}
- (void)addIndexPathToImageLoadingQueue:(NSIndexPath *)indexPath {
if (!indexPathsNeedingImages) {
indexPathsNeedingImages = [NSMutableSet set];
}
[indexPathsNeedingImages addObject:indexPath];
dispatch_async(myQueue, ^{ [self bg_loadOneImage]; });
}
Если ячейка перестает отображаться, удалите путь индекса ячейки из набора:
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
[indexPathsNeedingImages removeObject:indexPath];
}
Чтобы загрузить изображение, найдите путь указателя из набора. Мы должны отправить обратно в основную очередь для этого, чтобы избежать условия гонки на множестве:
- (void)bg_loadOneImage {
__block NSIndexPath *indexPath;
dispatch_sync(dispatch_get_main_queue(), ^{
indexPath = [indexPathsNeedingImages anyObject];
if (indexPath) {
[indexPathsNeedingImages removeObject:indexPath];
}
}
if (indexPath) {
[self bg_loadImageForRowAtIndexPath:indexPath];
}
}
Метод bg_loadImageForRowAtIndexPath:
такой же, как указано выше. Но когда мы действительно устанавливаем изображение в ячейке, мы также хотим удалить путь индекса ячейки из набора:
- (void)setImage:(UIImage *)image forItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = (MyCell *)[collectionView cellForItemAtIndexPath:indexPath];
// If cell is nil, the following line has no effect.
cell.imageView.image = image;
[indexPathsNeedingImages removeObject:indexPath];
}