UICollectionView reuse cell issue - ios

I have a problem with collection view cell. When my collection view first loaded, it display items like that:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
CalendarCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"CalendarCell" forIndexPath:indexPath];
[cell bindDate:_datesMgr.currentMonthItems[indexPath.row] andNowDate:_datesMgr.nowDate];
// bind events
if (_eventsMgr.eventsArray.count > 0){
for (int i = 0; i < _eventsMgr.eventsArray.count ; i ++) {
[cell bindConference:_eventsMgr.eventsArray[i]];
}
}
return cell;
}
Inside those methods are logic for adding subviews to custom cell class, which depend on certain circumstances.
Its all work, but, when collection view reloaded (i did force reload after 1 second) some of cells are reused and placed on others, therefore, it show "old" images and subviews.
I could see possible solution in forcing uicollection view to stop reusing cells (it is, load new cells every time). Is there any way to do this?

Try to implement prepareForReuse method for reset your old content in your custom UICollectionViewCell
-(void)prepareForReuse {
[super prepareForReuse];
self.yourimageview.image = nil; //and etc.
}

Related

Calling removeFromSuperview on subview in UICollectionViewCell does not work on first load, only when cell is reused

I have a collection view cell with a number of subviews. One of the subviews I want to remove altogether so the adjacent subview can expand to fill that space via constraints.
This constraint configuration set up within the prototype cell in storyboard, and the subview is already in place in the prototype, ready to be removed after the cell has been instantiated.
My UICollectionViewCell subclass has a setter that does this:
- (void)setThing:(NSString *)aThing
{
if (!aThing)
{
[self.thingContainerView removeFromSuperview];
return;
}
// proceed if aThing exists
And when the cell is setup:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"mycellid" forIndexPath:indexPath];
cell.aThing = nil; //this should trigger removal of one of the cell's subviews
return cell;
}
The problem is when the UICollectionViewCell is first loaded, the subview has not been removed. Once I've scrolled the cell offscreen and returned to it, the subview has been removed as expected. So I'm assuming there's some sort of issue with the cell not being fully laid out when I'm setting the property the first time around.
How to get this to work?
I don't think the view has been laid out at that point so the subview cannot be removed. Try removing it in a different method such as;
- (void) didMoveToSuperview
{
if (!self.aThing)
{
[self.thingContainerView removeFromSuperview];
}
}
This will be overridden in your collection view cell.
There might be a more appropriate method to call this in but this should work.

Changing the color of a label in a UICollectionViewCell only takes effect in one cell?

Introduction
I have a collectionView placed in my mainViewController with each DailyCell (custom UICollectionViewCell class) having two labels. I also have a Settings view controller that modifies the color of certain labels of the mainViewController, including those in the collectionViewController via a shared instance (singleton) of the viewController (.Vc). The settings pane changes the colors of all the other labels successfully, except for the ones present in the collectionView of which only two cells change.
Code
Here's the code for the collectionView in my mainViewController.m:
#pragma mark - UICollectionViewDataSource
// 2
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
// 1
return MIN([[WXManager sharedManager].dailyForecast count], 6) + 1;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
_cell = [collectionView dequeueReusableCellWithReuseIdentifier:DayCell forIndexPath:indexPath];
//Fetch Data
WXCondition *weather;
weather = [WXManager sharedManager].dailyForecast[indexPath.row];
averageWeather = (weather.tempHigh.floatValue +
weather.tempLow.floatValue)/2.0;
[_cell.labelTemp setText:[NSString stringWithFormat:#"%.0f°",
averageWeather]];
return _cell;
}
Here's the custom Daily Cell class code where the two labels are initialized:
if (self) {
s = [Singleton sharedInstance];
s.Dc = self;
// Initialize Label Day
[self.labelDay setTextColor:[UIColor lightGrayColor]];
[self.contentView addSubview:self.labelDay];
// Initialize Label Temp
[self.labelTemp setTextColor:[UIColor lightGrayColor]];
[self.contentView addSubview:self.labelTemp];
return self;
}
And here is the part in the settings where I try to change the color of those two labels depending on which static cell in the Settings is picked (i.e. when this cell is tapped in Settings, apply these colors to the labels in the viewController):
if ((indexPath.row == 1) && (indexPath.section==0)) {
//argentum
s.Dc.labelDay.textColor=[UIColor redColor];
s.Dc.labelTemp.textColor=[UIColor orangeColor];
}
if ((indexPath.row == 0) && (indexPath.section==0)) {
//album
s.Dc.labelDay.textColor=[UIColor lightGrayColor];
s.Dc.labelTemp.textColor=[UIColor lightGrayColor];
}
Visual Support
Question
Why is only one cell changing and not the rest?
Update 1
Continuously pressing a theme (sometimes) progressively updates the cell labels correctly. Still unsure what is causing this.
if indexPath.row is equal to zero means that the first element was selected (WED) if indexPath.row is equal to one then you're referring to the second element and so on. There's a method you should try: didSelectRowAtIndexPath
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
s.Dc.labelDay.textColor=[UIColor redColor];
s.Dc.labelTemp.textColor=[UIColor orangeColor];
}
I'm not completely sure tho, let me know!
My guess is that when you type s.Dc = self; in your daily cell, it removes the previous cell that you set at Dc property to replace it with the current one. And so you always have one cell linked to your singleton.
An other way to do it would be to store an enum (let's name it dayCellTheme) in you singleton. That you update when selecting a cell in your SettingsViewController.
Then in the cellForRowAtIndexPath datasource of your collectionViewController, you set the color of your labels accordingly to this property.
Now all you have to do is use a reloadData on your collection view when you leave the settingViewController.
Still i might be wrong about your problem. Let me know !

How can I make a Custom UITableViewCell top cell draw uniquely after reuse?

So I've made some custom table view cells and they draw correctly and look great, however once I scroll past the edge of the visible cells they start being reused, which is fine, except that when I scroll back the reused cells are still shown and don't redraw. Specifically all the cells look the same except for the top-most cell.
Pictures detailing the occurrence:
How I have this coded up, is when the cells get made if the indexPath.row is greater than 0 add an "overlap effect" which is just a gradient on a uiview placed underneath the custom drawing on the UITableViewCell's contentView.
This is how I add the overlap effect in the UITableViewController's tableView:cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"APostCell";
PostCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
CustomPost *aPost = [self.posts objectAtIndex:indexPath.row];
if (indexPath.row > 0) {
[cell addOverlap];
}
cell.postDateLabel.text = [aPost datePostedAsString];
return cell;
}
How would I implement this [cell removeOverlap]?
Try this
if (indexPath.row == 0) {
//remove overlap here
} else {
[cell addOverlap];
}
beacuse, except 1st cell all have overlap.On scrolling the reused cell have the overlap. So for first cell remove the overlap if present.
So after I posted the question I figured it out and, since I had the question and had previously not found any information on the subject figured I would share.
So whenever
PostCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]
is called, the table view either creates a new cell or reuses an old one. When a new cell is created and it is not the top cell (indexPath.row == 0) it adds the overlap to the UITableViewCell. And if it reuses the cell, that method still gets called, regardless what cell is being reused. So naturally once the cell created at the top is reused, the gradient view still gets added to cell.contentView and it stays there even when I'm reusing for the topmost cell again.
In fact adding the overlap view in this way will stack multiple overlap views into the same cell.
So what has to be done (if you intend to customize the cell appearance this way) is to remove the added views before each reuse of the cell. So you have to overwrite the custom tableviewcell's prepareForReuse method and do just that like so.
- (void) prepareForReuse {
[super prepareForReuse];
[self removeOverlap];
}
Be SURE the cell has the overlap view otherwise your app will break by trying to remove views not there. so have something like
- (void) removeOverlap {
if ([self.contentView.subviews count] > 1) {
//This method works based on the assumption [cell addOverlap] adds new view
//underneath existing views - like [self.contentView insertSubview:overlappedView atIndex:0];
[[self.contentView.subviews objectAtIndex:0] removeFromSuperview];
}
}

Wrong image load in uicollectionviewcell when scrollling

I am using a UICollectionview to show a lot of Custom cells (250 more or less).
Those cells have a main Image and some text. As the images have to be downloaded from the Internet I am using the external library AsyncImageView to do the lazy load stuff.
But the problem is that the reusable property of the cells are making me crazy.
When I scroll the images appear in the wrong cells. How can I add a tag or something to the images apart from the indexpath to avoid the problem?
Maybe AsyncImageView has a solution to the problem which I ignore ...
Or another alternative would be a better choice?
Any clue?
Thanks in advance
Edit: A simplified version of my code
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Cell";
CollectionComercioCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
if (cell == nil){
}
else{
[[AsyncImageLoader sharedLoader] cancelLoadingImagesForTarget: cell.myImage];
}
cell.myImage.imageURL = nil;
cell.myImage.image = nil;
cell.myImage.hidden = TRUE;
cell.myImage.imageURL = [[myArray objectAtIndex:indexPath.row] getLogoUrl];
cell.myText.text = [[myArray objectAtIndex:indexPath.row] getName];
cell.myImage.hidden = FALSE;
return cell;
}
CustomCell.m
- (void)prepareForReuse
{
[super prepareForReuse];
self.myImage.image = nil;
}
Make sure you set the image to nil in your cellForRowAtIndexPath: method. This way it will stay at least empty until the new image is loaded.
As you mentioned in your comments to the question that this is works if you scroll slow and not when you scroll fast you could maybe override - (void)prepareForReuse on your custom cell.
But be aware that Apple warns you not to use it for content changes:
For performance reasons, you should only reset attributes of the cell
that are not related to content, for example, alpha, editing, and
selection state. The table view's delegate in
tableView:cellForRowAtIndexPath: should always reset all content when
reusing a cell. If the cell object does not have an associated reuse
identifier, this method is not called. If you override this method,
you must be sure to invoke the superclass implementation.

UICollectionViewController - sometimes shows wrong cells, during fast scroll

I have an UICollectionViewController and my custom cells, and in my cellForRowAtIndexPath: method I set the cells based on indexPath.row.
But I am getting wrong results, this cell appears even after first position, and if you scroll back and forth, it pops up in random places. How do i fix that?
Here is the code:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
DVGCollectionViewCell *cell;
cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
if (indexPath.row == 0)
{
cell.imageView.image = [UIImage imageNamed:#"something1.png"];
cell.buyLabel.text = #"170";
cell.textLabel.text = #"11.2011";
}
return cell;
}
Cell in both UITableView and UICollectionView are recycled, that means that when one goes off screen it is put in an NSSet until you need it again. When it's need it's removed from the set ad added again at UICollectionView views hierarchy. If you do not clean the value inside the cell or set them again, the cell will show the same data when it was created.
This is made for performance reason creating cell takes more time instead of value them again.
If your problem is in layout check the layout flow object, which size did you set?
I have found the problem, once the cell contents was set it was never cleaned. So I added cleaning every cell properties as additional clause and it works fine.
You can perform any clean up necessary to prepare the view for use again if you override prepareForReuse in your custom cell implementation.
One of the answers in this SO post helped: override prepareForReuse and reset the cell to its default state. Don't forget to call super.prepareForReuse.

Resources