4

I have a UICollectionView that contains dynamic content. The problem I am having is that when the cell is dequeued it forgets its state. In my - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath I have a boolean value that when it is true, it changes to a true image, but when it is false, it changes to a false image. However when I scroll down and back up, the cells forget there state. Is there a way to make a cell remember its state? Here is what is inside my

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath   

    SalesCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    SaleImage * saleImage = [self.SaleObjs objectAtIndex:indexPath.row];
    cell.bgImageView.image = [UIImage imageNamed:saleImage.imageName];
    if (saleImage.isBookable) {
        cell.isBookable = YES;
    }
    else{
        cell.isBookable=NO;
    }

return cell;
}

I have a remedy for this but it effects performance. I add this to my custom cell;

-(void)prepareForReuse{
    [super prepareForReuse];


    [self setNeedsLayout];
}

Here is my collection view cell;

@implementation SalesCollectionViewCell


- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {

        // Initialization code
        self.layer.cornerRadius = 6.0;
        self.bgImageView = [[UIImageView alloc] init];
        self.book = [[UILabel alloc] init];      
        [self.contentView insertSubview:self.bgImageView atIndex:0];


         return self;
}

-(void)layoutSubviews{

    [super layoutSubviews];

    if (self.isBookable) {
        self.book.frame =CGRectMake(0, self.bounds.size.height - 41, 140, 41);
        self.book.text = @"Book this Item";
        self.book.textColor = [UIColor whiteColor];
        self.book.adjustsFontSizeToFitWidth=YES;
        self.book.textAlignment = NSTextAlignmentCenter;
        self.book.backgroundColor= [UIColor darkGrayColor];
        self.book.font = [UIFont fontWithName:kAppFont size:17.0];
        [self.contentView insertSubview:self.book atIndex:2];

        self.bgImageView.frame =CGRectMake(0, 0, 140, self.bounds.size.height - self.book.bounds.size.height);


    }
    else{
        self.bgImageView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);

    }



}

-(void)prepareForReuse{
    [super prepareForReuse];

    [self.book removeFromSuperview];
    [self setNeedsLayout];


}
DevC
  • 6,606
  • 9
  • 40
  • 80
  • 1
    Strange, your collection view doesn't have "dequeueReusableCellWithReuseIdentifier" in the cellForItem in the above source code. Your saleImage.isBookable property should already handle the state, is the isBookable property ever changed? – Zhang Jul 04 '14 at 09:48
  • @Zhang Sorry that was a typo, please see the above edit. The isBookable property is different for each cell. So some may be bookable, and others are not. What happens is when a bookable cell is reused with the contents of a nonbookable item, the bookable image remains. – DevC Jul 04 '14 at 13:03
  • @Zhang it correctly handles the bookable state when the views are present on the screen, but if i scroll down (so the top cells leave the screen) and scroll back, the non bookable objects have the bookable image. – DevC Jul 04 '14 at 13:05
  • Try setting your image = nil; before checking the if() condition in the cellForItemAtIndex, see if that clears the image. – Zhang Jul 05 '14 at 16:03
  • @Zhang Unfortunately it didnt help – DevC Jul 09 '14 at 13:22

6 Answers6

5

Cells should not "remember" their state; collection view or table view data source should. In the respective cellForIndexPath method, you should set the current state of the cell and let it configure itself as needed.

Leo Natan
  • 55,734
  • 8
  • 140
  • 186
2
- (void)prepareForReuse {
  [super prepareForReuse];
  self.isBookable = nil;
  [self.book removeFromSuperview];
  [self setNeedsLayout];
}

Try setting isBookable to nil. I'm assuming that the cell is setting its layout with the previous cell's isBookable value.

Yukai
  • 43
  • 9
2

As @iphonic said, the isBookable property should be (re)moved from the cell completely. Cells in a UICollectionView are being reused most of the time, so even though your saleImage.isBookable is in the correct state your cell.isBookable is probably not.

I would do the following:

if(saleImage.isBookable){
  self.bgImageView.frame = CGRectMake(0, 0, 140, self.bounds.size.height - self.book.bounds.size.height);
  cell.bgImageView.image = [UIImage imageNamed:saleImage.imageName];
  cell.book.hidden = NO;
}
else{
 self.bgImageView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
 cell.bgImageView.image = nil;
 cell.book.hidden = YES;
}

[cell layoutIfNeeded];

inside collectionView: cellForItemAtIndexPath:.

I would also have finished setting up the book UILabel inside initWithFrame and have it initially hidden. Something like:

- (id)initWithFrame:(CGRect)frame{
    self.layer.cornerRadius = 6.0;
    self.bgImageView = [[UIImageView alloc] init];
    [self.contentView insertSubview:self.bgImageView atIndex:0];

    self.book = [[UILabel alloc] initWithFrame:CGRectMake(0, self.bounds.size.height - 41, 140, 41)];      

    self.book.text = @"Book this Item";
    self.book.textColor = [UIColor whiteColor];
    self.book.adjustsFontSizeToFitWidth=YES;
    self.book.textAlignment = NSTextAlignmentCenter;
    self.book.backgroundColor= [UIColor darkGrayColor];
    self.book.font = [UIFont fontWithName:kAppFont size:17.0];
    self.book.hidden = YES;

    [self.contentView insertSubview:self.book atIndex:2];
}

Then you would not need to override layoutSubviews.

Hope that helps.

Don Miguel
  • 782
  • 5
  • 15
1

I'd override isBookable setter in the cell class:

- (void) setIsBookable:(BOOL)isBookable
{
    BOOL needsLayout = isBookable != _isBookable;
    _isBookable = isBookable;
    if(needsLayout)
    {
        [self.book removeFromSuperview];
        [self setNeedsLayout];
    }
}

Also I'd recommend change @property (nonatomic) BOOL isBookable; with @property (nonatomic, getter = isBookable) BOOL bookable; in order to follow Apple Code Convention.

Sviatoslav Yakymiv
  • 7,667
  • 2
  • 18
  • 42
1

You need to create two types of cells with different identifiers:

     static NSString *Bookable = @"Bookable";
     static NSString *NonBookable = @"NonBookable";
     NSString *currentIdentifier;
     if(saleImage.isBookable)//isBookable property must be set in SaleImage class
     {
          currentIdentifier = Bookable;
     }
     else{
          currentIdentifier = NonBookable;
     }


     SalesCollectionViewCell *cell = (SalesCollectionViewCell*)[collectionView dequeueReusableCellWithIdentifier:currentIdentifier];
Ritu
  • 651
  • 3
  • 8
0

1st - Save/keep the indexPath of the cell that is changed.

2nd -

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath   

    SalesCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];

    if (indexPath == self.changedIndexPath)
        [cell trueImage];
    else
        [cell falseImage];
return cell;
}

Inside the cell, you just implement the -(void) trueImage and the - (void) falseImage which changes the image inside the cell.

I hope I helped :)

Ricardo Anjos
  • 1,339
  • 17
  • 22