My UITableView has duplicated rows - iphone

Im not sure why, but my UITableView, which isnt anything fancy, is showing repeating rows when it shouldnt be.
It seems that the rows that get added when the user scrolls (i.e. the rows that are off the screen to start with) are getting the data for the wrong row index. Its almost like when a new cell is de-queued, it's using a cell that 'was' used, but wasn't cleaned up correctly.
Do you need to 'clean up' cells that are de-queue so that new cells dont use cells that are already created?
my code is as below:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CustomCellIdentifier = #"CustomCellIdentifier";
MyDayCell *cell = (MyDayCell *)[tableView
dequeueReusableCellWithIdentifier: CustomCellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MyDayCell" owner:self options:nil];
for (id oneObject in nib)
if ([oneObject isKindOfClass:[MyDayCell class]])
cell = (MyDayCell *)oneObject;
}
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSArray *thisSectionItems = (NSArray*)[self.listData objectForKey: [[NSNumber alloc] initWithInt:section]];
MyDayDetails *rowData = [thisSectionItems objectAtIndex:row];
//setup my cells data here...
return cell;
}
Is there anything wrong with this code?
has anyone seen anything like this before?

Cells are supposed to be reused. If you want to turn that off, turn off cell reuse.
Your problem is actually in the code you didn't include.
//setup my cells data here...
This code is responsible for completely loading every aspect of the cell that varies between the rows in your table. That data that's showing up more than once? You need to set it in cases where you have it, or clear it if you don't.
For instance:
cell.textLabel.text = str ? str : #"";
In that way, the same few cells are used over and over again, and table cells don't need to be set up and destroyed frequently.
(As I mentioned, you can turn off cell reuse. But you should make this work.)

I think i have solved it, do I just need to add an else statement to the if (cell == nil) block which cleans up the cells populated values?
Is this the correct way to go about doing this?

Related

Having trouble with UITableViewCell loaded from nib

I am having trouble wiht UITableViewCell loaded from nib. I used apple documentation to that but I do't know where I am going wrong. UITableViewCell created through IB contains 5 UIlable. Nib are loaded perfectly but the problem is when I scroll text of the label changes automatically to different.
Following is the my code for cell for row at indexpath. Please let me know where I am going worng.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
int rowNo = [indexPath row];
NSLog(#"Row No:%d",rowNo);
Transfer *tempTransferRecord =(Transfer*)[self.transferInformation objectAtIndex:rowNo];
seasonYear.text=tempTransferRecord.seasonYear;
longName.text=tempTransferRecord.longName;
transactionDate.text=tempTransferRecord.transactionDate;
toTeam.text=tempTransferRecord.toTeam;
fromTeam.text=tempTransferRecord.fromTeam;
/*
static NSString *MyIdentifier = #"MyIdentifier2";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier] ;
if (cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:#"PlayerTransfers" owner:self options:nil];
cell=transfersInfoCell;
self.transfersInfoCell=nil;
}
*/
static NSString *CellIdentifier = #"MyIdentifier2";
static NSString *CellNib = #"PlayerTransfers";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellNib owner:self options:nil];
cell = (UITableViewCell *)[nib objectAtIndex:0];
}
// perform additional custom work...
return cell;
}
Try moving your cell loading code to the top of cellForRowAtIndexPath, and the rowNo and value setting after that. Cells changing content must mean you're either changing what's in the data source between when you first set it and when you scroll, or you're setting values at the wrong point in time (i.e setting values on row 5 but then loading row 6 from a nib, or setting values on a row but then loading the nib for it afterwards so the values you set are overridden). Also, I'm a bit concerned that you set local variables rather than, say, (PlayerTransfers*)cell.seasonYear.text = tempTransferRecord.seasonYear or (UILabel *)[cell viewWithTag:1].text = tempTransferRecord.seasonYear. Shouldn't you be setting the values within each cell rather than on your table view class? That could explain why values are changing to something different: you may use the same variables to set every cell.
Have you set a CellIdentifier in Interface Builder?

(iPad/iPhone) refresh a table cell while it is on screen

I have a table view with custom cells, each of which have images and some text which must be parsed from a webpage. I have and operation queue which gets the data from the page and calls the method (void)addLoadedImageAndExcerpt:(NSString *)imgExc in the tableviewcontroller after each page's data is loaded and stores the data in 2 arrays. I need each cell to refresh once the image and text that associated with it are loaded into these 2 arrays (named "articleExcerpts" and "imageDataObjects").
the method is as follows:
- (void)addLoadedImageAndExcerpt:(NSString *)imgExc {
NSArray *imgAndExcerpt = [imgExc componentsSeparatedByString:#"{|}"];
[articleExcerpts addObject:[imgAndExcerpt objectAtIndex:1]];
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: [imgAndExcerpt objectAtIndex:0]]];
[imageDataObjects addObject:imageData];
//count how many rows have been loaded so far.
loadedCount ++;
[self.table reloadData];//table is a UITableView
[imageData release];
}
the problem is, I can't get the cells to change while they are on screen. Once I scroll, they show the proper data, while they are on screen, I can't get them to change. I tried the methods outlined here and here, but they don't work. I tried calling tableView:cellForRowAtIndexPath: for the relevant row and modifying the variables, but that didn't solve anything because that method seems to create a new cell every time is is called, and doesn't get the existing ones (I'll post the code for that method further down).
Using [self.table reloadData] as I have it now doesn't seem do anything either, which really confuses me...
my tableView:cellForRowAtIndexPath: method (I bet the problem is here. I'm not convinced I creating my custom cells properly)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomizedCell";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
for (id currentObject in topLevelObjects){
if ([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (CustomCell *) currentObject;
break;
}
}
}
// Configure the cell...
//title
cell.titleString = [titles objectAtIndex:indexPath.row];
//date
cell.dateString = [dates objectAtIndex:indexPath.row];
//Photo. check if imageDataObjects array is complete up to the current row yet
if (loadedCount > indexPath.row) {
if ([imageDataObjects objectAtIndex:indexPath.row] != #"NA") {
cell.imageData = [imageDataObjects objectAtIndex:indexPath.row];
} else {
cell.imageData = NULL;
}
}
//Excerpt. check if loadedCount array is complete up to the current row yet
if (loadedCount > indexPath.row) {
cell.exerptString = [articleExcerpts objectAtIndex:indexPath.row];
}
return cell;
}
what am I missing?
I have had a similar problem before, and was able to get it working by including the lines
[table beginUpdates];
[table endUpdates];
at the end of the method where your data is received (so call them once you have the data to populate the cells).
Hope this works for you too!
Hmm, I think you're only supposed to interact with UI components in the main thread. NSOperationQueue stuff runs in another thread. Instead of calling
[self.table reloadData]
try
[self.table performSelectorOnMainThread:#selector(reloadData:) withObject:nil waitUntilFinished:NO]
As far as I understand, the image is being loaded, and then added to an array of images (imageDataObjects), and the row never updates.
First things first, are you sure that the method addLoadedImageAndExcrept is adding the images in order? Remember that NSArray objects are nil-terminated, and therefore, if you're adding an image for a row further, it won't appear if a previous image is nil. What happens if an image comes nil? The array will end abruptly. Use the "count" method on the array to check if this happens, add dummy objects, or swtich to a dictionary. This may not solve your current issue, but it's something to consider. (*)
Aside from that, if images are being loaded correctly, the only reason for your code to not work (in what I understand from the code), is that the table IBOutlet you added, is not connected.
*EDIT: I noticed that you're checking for #"NA" on the row (although I don't see where it's being set), so you probably already considered that

Using multiple custom cells without reusing identifier

I feel like a real noob asking this, but here's my problem:
I want to show a tableView, with 7 custom cells. None of these cells is reused. That means the user will see 7 different cells, not more, not less.
I created the cells in the viewDidLoad method, and added all those cells in the listCells-array. After that, I used easy-mode to draw those cells:
UITableViewCell *cell = nil;
if (indexPath.row == 0)
{
static NSString *MyIdentifier = #"Cell";
cell = (DetAlertCell *)[localTableView dequeueReusableCellWithIdentifier:nil];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"DetAlertCell" owner:self options:nil];
cell = [listCells objectAtIndex:indexPath.row];
}
}
....
However, this won't work. It shows me a blank view. Every cell is created using a .xib-file and a .h and .m class. Is there anything that I'm missing and should do?
Just don't call the [localTableView dequeueReusableCellWithIdentifier:nil] and loa the correct cell for the index path.
Also you say that you load the cells in the viewDidLoad, then why do you load the nib:
[[NSBundle mainBundle] loadNibNamed:#"DetAlertCell" owner:self options:nil];
They should already be the array should they not.
And why to you check if the row is 0 then load the row, still will only load the first row.
Try this:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return [listCells objectAtIndex:indexPath.row];
}

iphone : uitableview : contents of a cell change on scrolling

I have an array which I am using to furnish the contents of custom cell in table view
I dont know whats wrong but when I scroll a tableview the contants oc cell changes dynamically
Please help me to fix this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
MyIdentifier = #"tblCellView";
NSString *offendersImagePath = [self applicationDocumentsDirectory];
//NSLog(#"%#", dbPath);
offendersImagePath=[offendersImagePath stringByAppendingPathComponent:#"Images"];
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if(cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = aCustomCell;
aCustomCell=nil;
}
NSMutableArray *tempArray;//=[[NSMutableDictionary alloc] init];
tempArray=[offendersNamesList objectAtIndex:indexPath.row];
//NSLog(#"%d",indexPath.row);
offendersImagePath=[offendersImagePath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.jpg",[tempArray objectAtIndex:0]]];
NSLog(offendersImagePath);
[[cell offendersImageView] setImage:[UIImage imageWithContentsOfFile:offendersImagePath]];
[cell.offendersNameLbl setText:[tempArray objectAtIndex:1]];
[cell.offendersViolation setText:[tempArray objectAtIndex:2]];
//[tempDictionary release];
//[cell setLabelText:[arryData objectAtIndex:indexPath.row]];
return cell;
}
What is typically the problem in these kinds of problems is that you are not setting up the content in your cell correctly in the cellForRowAtIndexPath. When a cell scrolls off the screen the system dumps it into a "Recycle Queue" (my term). As new cells scroll onto the screen the system looks in this recycle queue for cells it can reuse.
((CustomCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];)
If it does not find one it goes ahead and builds an entirely new one from scratch. In your case, it looks like for whatever reason you are not setting up the cell content correctly and the changes you are seeing are recycled cells that have not been updated with the correct content.
I'm not sure exactly where you are going wrong but the code you are using for new cells is a little strange. It should look more like this:
if(cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
I'm not 100% what your issue is, but if it's items that have variable text length and the items on the cell aren't getting re-sized, it's because the sizings of the labels contained in the cell are not getting updated, just their text is getting changed.
A note about doing lookups in the cellForRowAtIndexPath, perhaps you should construct your array(of objects/dictionaries) in viewDidLoad, so as to be more efficient.
Another thing to note is your using .jpg files. It's more optimal to use .png files as they are crushed in size at compile time.
Also using NSMutableArray *tempArray; and hard-coded indexes is very bad practice, if something changes position in the array it means you have to change all your code. Try using a NSDictionary so as the keys are less likely to change.

TableView not populating until I scroll past the cell when height of cell is changed

What I've done is created a custom TableCell view that gets populated with information from an array of objects. Each TableCell gets loaded in the
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
Cell *cell = (Cell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"Cell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
NSUInteger row = [indexPath row];
NSDictionary *rowData = (NSDictionary *)[self.surveys objectAtIndex:row];
cell.info1.text = [rowData objectForKey:#"info1"];
cell.info2.text = [rowData objectForKey:#"info2"];
cell.info3.text = [rowData objectForKey:#"info3"];
cell.otherInfo = [rowData objectForKey:#"otherInfo"];
return cell;
}
In addition to this I specify a custom height for the cell here
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 76.0;
}
When the value for tableView: heightForRowAtIndexPath: is 76 it loads all the cells that I can see and they aren't ever blank. Without changing anything else in the code I can modify the value in tableView: heightForRowAtIndexPath: and it will only show the first cell value when the view is loaded. As I scroll down they are refreshed with a value as soon as their top edge hits the top of the screen. When I get back up to the top and stretch the view so that it bounces back the bottom ones will disappear.
As far as I can tell, the change in height somehow affects how they are loaded but I can't for the life of me see how.
I specified a height in the custom cell .xib that was different than the one I was specifying in code. Once this was changed to match the size specified in the code the problem went away.
Another thing to pay attention to isn't just the size of the Custom table view cell's height but also the height of other sub views as well.
I had this problem when one of my UIImageView objects was higher than the cell height it would cause this very same problem.
Thanks for the post as it helped me sort out my issue too. +1 (wont let me vote up or I would, sorry)