Simple UICollectionView to show images behaves odd: Some Images are displayed correct, some at wrong position, some missing at all

18,698

In the meantime, I've found the solution. It was actually a very subtle error in the implementation of my UICollectionViewCell subclass AlbumCoverCell.

The problem is that I've set the frame of the cell instance as the frame of the UIImageView subview instead of passing the bounds property of the cell's contentView!

Here is the fix:

@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
Share:
18,698

Related videos on Youtube

Tafkadasoh
Author by

Tafkadasoh

I'm a German software and UX engineer, working right now for DASGIP GmbH, a biotechnology company belonging to the Eppendorf group. DASGIP develops and manufactures technologically superior parallel bioreactor systems designed for the lab-scale cultivation of microbial, plant, animal and human cells. I've previously worked as an iOS developer for NeoMedia Technologies on their products NeoReader -- a QR and barcode reader -- and the NeoReader SDK. I'm mainly interested in Objective-C programming, and I like to code elegant and terse, yet with a high readability and good code structure.

Updated on August 09, 2022

Comments

  • Tafkadasoh
    Tafkadasoh almost 2 years

    I want to show images in a grid on iPhone using a UICollectionView, showing 3 images a row. For a "dead simple test" (as I thought), I've added 15 JPG images to my project, so that they'll be in my bundle and I can load them simply via [UIImage imageNamed:...].

    I think I've done everything correct (setting up & registering UICollectionViewCell subclass, use of UICollectionViewDataSource Protocol methods), however, the UICollectionView behaves very weird:

    It shows only a few of the images in the following pattern: First line shows image 1 & 3, second line is blank, next line like the first again (image 1 & 3 showing properly), fourth line blank, and so on...

    If I push a button in my NavBar that triggers [self.collectionView reloadData], random cells appear or disappear. What drives me nuts is that it's not only an issue of images appear or not. Sometime, images also swap between the cells, i.e. they appear for a indexPath they are definitely not wired up!

    Here is my code for the cell:

    @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
    

    Part of the code for my UICollectionViewController subclass, where 'imageNames' is an NSArray holding all jpg filenames:

    - (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);
    }
    

    From the NSLog statement in cellForItemAtIndexPath: I can see that the method is called for all of the cells (not only the one's displayed) and that the mapping between indexPath.row and filename is correct.

    Has anybody an idea what could cause this weird behavior?

  • krider2010
    krider2010 about 11 years
    I made a similar mistake - thanks for posting up the solution you found!
  • Jake Graham Arnold
    Jake Graham Arnold almost 11 years
    @Tafkadasoh Thank you! It fixed my problem instantly, I was starting to hit my head against the wall..
  • GangstaGraham
    GangstaGraham almost 11 years
    @Tafkadasoh OMG! Thank you so so much! Fixed my problem too. It's the smallest thing, but it was such an annoying bug. Thanks once again for posting this. I really really appreciate it.
  • Form
    Form almost 11 years
    Great, fixed my issues too. Thank you!
  • Tafkadasoh
    Tafkadasoh over 10 years
    @totocaster The UIImageView isn't nilled, it's the image property of the UIImageView.
  • alloc_iNit
    alloc_iNit about 10 years
    @Tafkadasoh - By doing nil-ing image property of UIImageView, it is not displaying the placeholder image also.!!
  • Carlo
    Carlo about 10 years
    It worked for me, thanks! Although do you know why that makes a difference?
  • Sam
    Sam about 9 years
    Good god almighty, spent 8 hours on this problem and your answer pointed me in the right direction.
  • tepl
    tepl over 6 years
    @Carlo The difference is in the origin of these two CGRects. Initializer argument frame has origin different from (0, 0) depending on cell location in collection view, while contentView.bounds has origin always at (0, 0) for any cell. I discovered it with debugging.