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.
Related
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.
}
I am trying to implement rating stars by using kDRATING VIEW .i have used following code in cellForRowAtIndexpath method but it causes my app to become slower.
If it try to allocate and initialise this in viewdidload method then it when i scroll up and down ,the stars fluctuates . please help in this regard
self.rating = [[KDRatingView alloc] initWithFrame:CGRectMake(0, 0, 60,20)];
[self.rating rateKDRatingView:2.80 outOf:3.0];
[cell.rating addSubview:self.rating ];
return cell;
It sounds like you need to look into UITableViewCell reuse because when you scroll a UITableViewCell out of the screen it will call cellForRowAtIndexPath again to remake this cell when it is back in view and that can cause flickering and memory consumption.
You are adding the KDRatingView to the rating view on the cell so I guess you have a custom cell, so why not instead have the KDRatingView inside the custom cell and just update its value when you need to.
Try this solution with some cell reuse:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"RatingCell";
RatingCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
// initialisation code
cell = [RatingCell new];
}
// setting code
[cell setRatingViewValue:2.8 outOf:3.0];
}
That way it only creates the cell once, so it won't slow down your app. Then each time the cell would have been recreated it just updates the cells rating (and anything else you need to set) instead.
Then you just need to implement the setRatingViewValue:outOf: method in your custom cell to update the KDRatingView that you need to have added and positioned in your custom cell upon initialisation.
I have a custom cell for my UITableView ; that custom cell comes from a class that extends UITableViewCell.
There is only a .xib where i crated the actual cell, and the links to these items in the .h.
I'm not using the .m, it's only the autogenerated awakeFromNib and setSelected:selected:animated in there.
When i create my cell in the cellForRow method, it's all fine, it appears correctly on the screen.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CustomCellTableViewCell";
CustomCellTableViewCell *cell = [self.tbUpcomingEvents dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
tmp = [_eventList objectAtIndex:indexPath.row];
cell.lbDescription.text = tmp.description;
cell.lbTitle.text = tmp.title;
cell.imageView.layer.cornerRadius = 20;
cell.imageView.clipsToBounds = YES;
cell.layer.masksToBounds = YES;
cell.imageView.image = [UIImage imageNamed:tmp.type.typeImage];
cell.lbTimeStart.text = [[[timeFormatter stringFromDate:tmp.startTime] stringByAppendingString:#" - "]stringByAppendingString:[dateFormatter stringFromDate:tmp.startDate]];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
My issue is the following : when I start scrolling the tableview, the NEW cells are messed up, specifically the image. It simply gets bigger/wider and (obviously) deformed. I don't really know what caused it or how to fix it.
The cell is associated with the reuse identifier from the cellForRow method and the custom class in the .xib file. The UITableView has nothing in particular (and has no other link than the storyboard -> .h, delegate, datasource)
Any clue?
I found my issue. In this specific case, my imageView in my customCell.h was called "imageView". Which is a reserved named and, i guess, created conflicts at (some?) points in the creation of the cells.
I don't know the why's, but i know what fixed the issue. Now i've simply put another property name for my image and i'm golden ;)
Try to replace your code:
cell.lbDescription.text = tmp.description;
cell.lbTitle.text = tmp.title;
cell.imageView.image = [UIImage imageNamed:tmp.type.typeImage];
cell.lbTimeStart.text = [[[timeFormatter stringFromDate:tmp.startTime] stringByAppendingString:#" - "]stringByAppendingString:[dateFormatter stringFromDate:tmp.startDate]];
from cellForRowAtIndexPath to willDisplayCell method.
The idea is to make UITableViewCells templates in tableView:cellForRowAtIndexPath:, but to fill cells with particular values in tableView:willDisplayCellForRowAtIndexPath:.
uncheck autoresize subviews in interface builder
I am trying to create a project with a custom UITableViewCell. The custom cells never load, they're just blank. At this point in the project what I'm trying to do is placing a UITableViewCell in a .xib, designing it the way I want and specifying its reuse identifier along with tag IDs for the components so that I can use them in code later on.
I've googled a ton and found several tutorials that look like what I want to do, along with many SO questions that have answers that seem applicable. At this point it's probably just my head spinning with too many different angles and solutions.
This is my current attempt at trying to register the custom cell with my UITableView, yet when running this on a device the rows in the table view are entirely blank.
UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MyCell"];
}
UILabel* l1 = (UILabel*)[cell viewWithTag:1];
UILabel* l2 = (UILabel*)[cell viewWithTag:2];
UILabel* l3 = (UILabel*)[cell viewWithTag:3];
l1.text = #"Foobar";
l2.text = #"Foobar";
l3.text = #"Foobar";
I'm pretty certain that I've hooked up all the properties and such correctly, but at this stage I need a fresh pair of eyes to point out the facepalm for me.
The interesting files are FilmerView.m/h/xib and the cell is in FilmerViewCell.xib. When running the app this TableView is in the second tab of the tab bar controller.
Project:
http://speedy.sh/WhhpP/test12.zip
I can't provide a full answer atm but look up the tableview method. registerNib:forCellReuseIdentifier:
Also, stop using that dequeue method. Use the one that includes the indexPath.
Then you don't have to check if the cell is nil afterwards.
EDIT
In viewDidLoad (or somewhere like that)...
UINib *cellNib = [UINib nibWithNibName:#"MyCustomCellXibFileName" bundle:[NSBundle mainBundle]];
[self.tableView registerNib:cellNib forCellReuseIdentifier:#"CellIdentifier"];
Now in the table view datasource method...
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier" forIndexPath:indexPath];
// no need to check cell == nil.
// this method is guaranteed to return a non nil cell.
// if it doesn't then the program will crash telling you that you need to...
// register a class or nib (but we just did this in viewDidLoad) :D
// configure your cell here...
[self configureMyCell:(MyCustomCell *)cell atIndexPath:indexPath];
return cell;
}
- (void)configureMyCell:(MyCustomCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
cell.nameLabel.text = #"Hello, world";
}
Hope this helps.
Make sure that you have set datasource and delegate properties of your tableView.
Make sure that you have implemented (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section method and it returns a positive value (>0).
Evaluate the following:
Is the ReuseIdentifier set in the XIB. See Properties Tab in Interface Builder on the right when selecting the cell.
Are the AutoresizingMasks set properly for the labels to be visible?
WrapModes: Which did you select? When having wrapmode WrapWord, is the font size too large for the text to be moved in the next line becoming invisible?
Set the background color of the UITableViewCellss content view to something else than white or transparent, as well as the background colors of the labels to see if the cell is even there.
Manually call numberOfRowsInSection on your table, pass the proper NSIndexPath identifying the target section and see if its greater 0 to confirm that the TableView even attempts to load the data, thus, the cells. ( Alternatively set a breakpoint in the method or do a NSLog. )
Do a NSLog in cellForRowAtIndexPath to confirm that the cell returned is not nil and the method is even called!
I am having a problem with a UIImageView I am setting inside tableview:cellForRowAtIndexPath: for some reason when the UITableView loads the bottom cell half in view dose not have the a Image loaded into the UIImageView like the rest of the UITableViewCells, however once I scroll then it works itself out.. But once doing so the top UItableViewCell then drops its image! untill i scroll that back into full view.
What I have done is created the UITableViewCell in Interface Builder and i have a blank UIImageView, I then set the UIImage I am going to place inside the UIImageView in ViewDidLoad then inside tableview:CellForRowAtIndexPath: I set it then return the cell.
heres the code
//.m
- (void)viewDidLoad
{
//..
storageCalcIconImage = [UIImage imageNamed:#"CALC.png"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
SeriesSearchResultItem *myObj = [[SeriesSearchResultItem alloc] init];
if (indexPath.section == 0) {
//call obj array with all of the values from my sorting algorithum
myObj = (SeriesSearchResultItem*)[dataArrayOfObjects objectAtIndex:indexPath.row];
//.. assinging storageCalcIconImage inside if statment
firstSpaceImage.image = storageCalcIconImage;
//..
}
return cell;
}
there is lots happening in tableView:CellForRowAtIndexPath but I decided to drop the stuff that wasnt related to the image problem.. hopefully you guys can see an error I am making that I havent yet... any help would be greatly appreciated.
This is an expected result from the use of reusable cells inside a tableView. If you want to avoid this behavior you should create a cell for each rows of your tableView instead of using dequeueReusableCellWithIdentifier.
Expect huge performance impact if you have a lot of rows to display though.