Сессия 211 — Создание параллельных пользовательских интерфейсов на iOS, с WWDC 2012, обсуждает проблему изменения изображения ячейки по мере того, как фоновые задачи догоняют (начиная с 38:15).
Вот как вы решаете эту проблему. После того, как вы загрузили изображение в фоновую очередь, используйте индексный путь, чтобы найти текущую ячейку, если таковая имеется, которая отображает элемент, содержащий это изображение. Если вы получаете ячейку, установите изображение ячейки. Если вы получите nil, в данный момент нет ячейки, отображающей этот элемент, поэтому просто выбросьте изображение.
- (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];
}
person
rob mayoff
schedule
17.12.2012