DevC
DevC

Reputation: 7022

UICollectionView with dynamic cell content - get cell to remember state

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];


}

Upvotes: 4

Views: 4020

Answers (6)

Ritu
Ritu

Reputation: 671

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];

Upvotes: 1

Sviatoslav Yakymiv
Sviatoslav Yakymiv

Reputation: 7935

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.

Upvotes: 1

Don Miguel
Don Miguel

Reputation: 912

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.

Upvotes: 2

Ricardo Anjos
Ricardo Anjos

Reputation: 1427

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 :)

Upvotes: 0

Yukai
Yukai

Reputation: 43

- (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.

Upvotes: 2

Léo Natan
Léo Natan

Reputation: 57060

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.

Upvotes: 6

Related Questions