Простой UICollectionView для отображения изображений ведет себя странно: некоторые изображения отображаются правильно, некоторые в неправильном положении, некоторые вообще отсутствуют

Я хочу отображать изображения в сетке на iPhone с помощью UICollectionView, показывая 3 изображения в строке. Для «мертвого простого теста» (как я думал) я добавил в свой проект 15 изображений JPG, так что они будут в моем пакете, и я могу загрузить их просто через [UIImage imageNamed: ...].

Думаю, я все сделал правильно (настройка и регистрация подкласса UICollectionViewCell, использование методов протокола UICollectionViewDataSource), однако UICollectionView ведет себя очень странно:

Он показывает только несколько изображений по следующему шаблону: первая строка показывает изображения 1 и 3, вторая строка пуста, следующая строка снова похожа на первую (изображения 1 и 3 отображаются правильно), четвертая строка пуста и т. Д. .

Если я нажимаю кнопку на панели навигации, которая запускает [self.collectionView reloadData], случайные ячейки появляются или исчезают. Что меня бесит, так это то, что проблема не только в том, появляются изображения или нет. Иногда изображения также меняются местами между ячейками, то есть они появляются для indexPath, они определенно не связаны!

Вот мой код для ячейки:

@interface AlbumCoverCell : UICollectionViewCell
@property (nonatomic, retain) IBOutlet UIImageView *imageView;
@end

@implementation AlbumCoverCell
@synthesize imageView = _imageView;
- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        _imageView = [[UIImageView alloc] initWithFrame:frame];
        [self.contentView addSubview:_imageView];
    }
    return self;
}

- (void)dealloc
{
    [_imageView release];
    [super dealloc];
}

- (void)prepareForReuse
{
    [super prepareForReuse];
    self.imageView.image = nil;
}
@end

Часть кода для моего подкласса UICollectionViewController, где imageNames - это массив NSArray, содержащий все имена файлов jpg:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.collectionView registerClass:[AlbumCoverCell class] forCellWithReuseIdentifier:kAlbumCellID];
}

#pragma mark - UICollectionViewDataSource Protocol methods
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return [self.imageNames count];
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    AlbumCoverCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kAlbumCellID forIndexPath:indexPath];
    NSString *imageName = [self.imageNames objectAtIndex:indexPath.row];
    NSLog(@"CV setting image for row %d from file in bundle with name '%@'", indexPath.row, imageName);
    cell.imageView.image = [UIImage imageNamed:imageName];

    return cell;
}

#pragma mark - UICollectionViewDelegateFlowLayout Protocol methods
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
{
    return CGSizeMake(100, 100);
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
{
    return UIEdgeInsetsMake(0, 0, 0, 0);
}

Из оператора NSLog в cellForItemAtIndexPath: я вижу, что метод вызывается для всех ячеек (а не только для отображаемой) и что сопоставление между indexPath.row и именем файла правильное.

Кто-нибудь знает, что может вызвать такое странное поведение?


person Tafkadasoh    schedule 06.12.2012    source источник


Ответы (1)


А пока я нашел решение. На самом деле это была очень тонкая ошибка в реализации моего UICollectionViewCell подкласса AlbumCoverCell.

Проблема в том, что я установил фрейм экземпляра ячейки как фрейм UIImageView subview вместо того, чтобы передавать свойство bounds contentView ячейки!

Вот исправление:

@implementation AlbumCoverCell
@synthesize imageView = _imageView;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // WRONG:
        // _imageView = [[UIImageView alloc] initWithFrame:frame];

        // RIGHT:
        _imageView = [[UIImageView alloc] initWithFrame:self.contentView.bounds];
        [self.contentView addSubview:_imageView];
    }
    return self;
}


- (void)prepareForReuse
{
    [super prepareForReuse];

    // reset image property of imageView for reuse
    self.imageView.image = nil;

    // update frame position of subviews
    self.imageView.frame = self.contentView.bounds;
}

...

@end
person Tafkadasoh    schedule 21.01.2013
comment
Я совершил аналогичную ошибку - спасибо, что опубликовали решение, которое вы нашли! - person krider2010; 18.03.2013
comment
@Tafkadasoh Спасибо! Это сразу устранило мою проблему, я начал биться головой об стену .. - person Jake Graham Arnold; 19.06.2013
comment
@Tafkadasoh OMG! Огромное спасибо! Исправлена ​​и моя проблема. Это самая маленькая вещь, но это была такая досадная ошибка. Еще раз спасибо за размещение этого сообщения. Я очень, очень ценю это. - person GangstaGraham; 20.06.2013
comment
Отлично, мои проблемы тоже исправлены. Спасибо! - person Form; 23.07.2013
comment
привет @Tafkadasoh, можешь мне сказать, что такое ContentView? - person R. Dewi; 18.09.2013
comment
Используя nil-ing image view, вы побеждаете концепцию повторного использования ячеек while. Таким образом, UIImageView всегда будет выделять себя перед рисованием на экране. - person totocaster; 20.10.2013
comment
@totocaster UIImageView не обнуляется, это свойство изображения UIImageView. - person Tafkadasoh; 25.10.2013
comment
@Tafkadasoh - Выполняя нулевое значение свойства изображения UIImageView, он также не отображает изображение-заполнитель. !! - person alloc_iNit; 24.04.2014
comment
У меня сработало, спасибо! Хотя знаете, почему это имеет значение? - person Carlo; 21.05.2014
comment
Боже всемогущий, потратил 8 часов на эту проблему, и твой ответ указал мне правильное направление. - person Sam; 19.05.2015
comment
@Carlo Разница в происхождении этих двух CGRects. Аргумент инициализатора frame имеет источник, отличный от (0, 0), в зависимости от местоположения ячейки в представлении коллекции, в то время как contentView.bounds всегда имеет начало в (0, 0) для любой ячейки. Обнаружил при отладке. - person tepl; 13.09.2017