Reputation: 7022
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
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
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
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
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
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
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