UITableview mixes up cells - iphone

i have a problem with my UITableview. After scrolling or pushing the switch it starts sometimes mixing up the cells. So you can so the text of one cell shows up in the other. I read a lot about tableViews, but I found nothing that fits for my code.
Here it is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
if (indexPath.section == 0) {
if (indexPath.row == 0) {
cell.textLabel.text = NSLocalizedString(#"Course:",#"Course Section");
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.detailTextLabel.text = entryTableCourseName;
}
else if (indexPath.row == 1) {
cell.textLabel.text = #"Due Date:";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.detailTextLabel.text = entryTableDueDateString; }
}
else if (indexPath.section == 1) {
if (indexPath.row == 0) {
//cell.textLabel.text = NSLocalizedString(#"Title", #"entryTableTaskTitle");
[cell.contentView addSubview:nameField];
}
else if (indexPath.row == 1) {
[cell.contentView addSubview:TaskView];
}
}
else if (indexPath.section == 2) {
if (entryTableGradeSwitch.on) {
if (indexPath.row == 0) {
cell.textLabel.text = NSLocalizedString(#"Grading", #"GradingCell");
cell.accessoryView = entryTableGradeSwitch;
}
else if (indexPath.row == 1) {
cell.textLabel.text = NSLocalizedString(#"Options", #"GradingCellOptions");
}
}
else {
cell.textLabel.text = NSLocalizedString(#"Grading", #"GradingCell");
cell.accessoryView = entryTableGradeSwitch;
}
}
}
I feel like I'm doing something really wrong on setting up the cells.

It's all due to cell-reusability. There are plenty of posts about this on StackOverflow, and I would recommend that you search for "UITableViewCell Reusability Problems", and you will come across the solution.
In short .. What happens here is that when you are scrolling, the cell essentially re-uses the one used before at that indexPath, and cellForRowAtIndexPath assigns a different cell at that indexpath.
Solution ?
Use different reuse identifiers for each cell.
Subclass your tableViewCell with the view or label that is getting messed up.

Make sure you implement
- (void) prepareForReuse
in your cell if you reuse them. In this routine clean up anything you added to the cell that you don't want to show up in another reuse. If you don't add any custom items just make sure when you populate a cell that you set everything even if it's empty.

Every time you do this:
[cell.contentView addSubview:TaskView]
Or something like it, you are adding a new subview to the cell. As the cells get reused, you add more and more subviews.
From your code, it isn't even clear where nameView and taskView come from. They should be either added once when a cell is first created, and just configured thereafter, or they should be part of your cell as designed in the xib or storyboard.

Here's a super-simple fix for this, and you still get to keep your reuse-identifiers! Also no subclassing or anything needed.
It's simple: just make sure and set all of your labels and views. This means that if one cell isn't going to use the detail text label, still set it to #"". By doing this, you make sure that no leftover data from the content that used the cell before is still there.
Super simple, and works every time! Also way better resource wise than not reusing your cells.

Related

UITableView Reuse Causing Broken Cells

When scrolling my UITableView (tends to be when I scroll it fast) the data for my cells gets mixed up, so labels might be repeated etc.
I understand that reusing the cells probably causes this, but what if the user scrolls down the list really quickly and all the cells get mixed up, how am I supposed to avoid this?
Thanks.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"VideoListCell";
VideoListCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[VideoListCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
if (isPlaylistView)
{
//Fill cell with data
}
else if (isPlaylistDetailView || isSearchResultsView)
{
//Fill cell with data
}
else
{
//Playlist button and uploads
if (indexPath.section == 0)
{
//Fill cell with data
}
else
{
//Fill cell with data
}
}
return cell;
}
You generally use this kind of code:
cell = dequeReusableCell;
if (cell == nil) {
create cell;
initialize cell;
}
fill cell with actual data from current row
return cell;
If you will move code "fill cell with actual data from current row" into "if" — you will get the kind of behavior you get right now.
So the answer will be "fill cell with data after you initialize it, outside of "if (cell == nil)" block.
UITableView will ever only dequeue a cell for reuse if the position that the cell was in is currently off-screen. So you don't have to worry about "mix-ups".
static NSString *cellIdentifier=#"cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell==nil)
{
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
}
I think it will be helpful to you.
set dequeueReusableCellWithIdentifier to nil for example..
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
UPDATE:
See this Example... i load many data in the cell with also my custom Gridview...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* cellIdentifier = #"gridCell";
UITableViewCell *gridCell = [tableView dequeueReusableCellWithIdentifier:nil];
if(gridCell == nil)
{
gridCell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
return gridCell;
}
hope this help you....
In your custom cell class override prepareForReuse method. In this method set text of your labels to nil and set imageview's image to nil also. This function is called everytime when a cell is reused so your problem will be solved by this. May be like this
- (void)prepareForReuse{
[super prepareForReuse];
self.titleLabel.text = nil;
self.unitImageView.image = nil;
}

UITableView moves cell contents around when scrolling

I have seen other questions on here related to this, but I have their solutions in place, and things still move around when I scroll.
Basically, I have a table of data and I want the first and only the first row to have a UITableViewCellAccessoryDisclosureIndicator. The problem is, when I scroll around, the indicator duplicates, deletes, and moves to other cells. Here is my code..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if(indexPath.row == 0) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.textLabel.text = [tabledata objectAtIndex:indexPath.row];
return cell;
}
The problem is something with the indexPath.row variable.. its somehow returning 0 in other cells that aren't the very first (when it comes to displaying cells). HOWEVER... the data is always right from my array(which implies the value HAS to be correct) and in my
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
method, i only wish to change screens when row 0 is clicked.. and regardless of where I have scrolled to, no cells trigger except the very first. So it seems THAT check is always correct while the one inside cellForRowAtIndexPath is not...
Most likely, you are re-using cells that already have the accessoryType set. You need to explicitly set it to NOT show on cells where it shouldn't.
if(indexPath.row == 0){
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}

how to define uitableviewcells

I am creating a uitableview that has two sections, section == 0 has 5 rows, and section == 1 has 1 row.
I have also declared several functions in an objective-c class that I would like to hook into each of the five rows. However I am not sure how to implement this.
I am thinking it is something like
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
cell.selectionStyle = UITableViewCellSelectionStyleNone;
//---------- In here put a bunch of IF statments, declaring each cell? and linking it to
//---------- the function I have declared in the objective-c class
return cell;
}
Usually the cells are setup in tableView:cellForRowAtIndexPath:. Determining the function that's called when a cell is tapped is usually done in tableView:didSelectRowAtIndexPath.
When setting up the cell in tableView:cellForRowAtIndexPath: you can specify the section and row using indexPath like this:
if(indexPath.section == 0){
if(indexPath.row == 0){
// setup this cell...
}else if(indexPath.row == 1){
}// ...
}else if(indexPath.section == 1){
if(indexPath.row == 0){
}else if(indexPath.row == 1){
}// ...
}
Do something similar in tableView:didSelectRowAtIndexPath when determining which function is called when the user taps each cell. The relevant documentation from Apple can be found here.
Typically this function would be configuring the cell by pulling data out of an array, database, or some other indexed collection:
if ([indexPath section] == 0) {
[[cell textLabel] setText:[sectionOneValues objectAtIndex:[indexPath row]]];
}
else if ([indexPath section] == 0) {
[[cell textLabel] setText:[sectionTwoValues objectAtIndex:[indexPath row]]];
}
In your case it sounds like you want to "hook into" some other functions for the 5 rows in section 0, so you'd be doing something other than using a sectionOnValues array in the if clause. It's a little hard to be more specific without knowing what you mean to do with these functions that hook in.
Depends, are all the cells equal, but only changing their content? In that case, you would be right.
On the other hand, if you plan on adding more Labels, images, or whatnot into the cell, then, for each different row, you would have to tweak the way the cells are being created, therefore, having to add new CellIdentifiers to avoid mixing cells, and changing the code inside the if (cell == nil) {} to configure each cell design.

Programmatically scrolling to and coloring a UITableViewCell at runtime

I have UITableViewController which contains a list of items. Now, I want the list to be automatically scrolled to a item (index = bestOne ) once the view appears. Meanwhile I want the item to be colored into red and be labeled as Marked.
My code roughly achieves what I want. But, I actually see more than one red items iterating: every 10 items, there is a red item.
I am quite new to iphone development, I figure it might have something to do with reusable cells. But I am not exactly sure why. Can anybody suggest one way to solve this issue? Thanks in advance.
(void)viewDidAppear:(BOOL)animated
{
if (self.bestOne != -1)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.bestOne inSection:0];
[self.tableView scrollToRowAtIndexPath: atScrollPosition: animated:YES];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [self.array objectAtIndex:indexPath.row];
if (indexPath.row == self.bestOne)
{
cell.detailTextLabel.text = #"Marked";
cell.textLabel.textColor = [UIColor redColor];
}
return cell;
}
You are right about the reusable cells part.
Your code should be something like –
cell.textLabel.text = [self.array objectAtIndex:indexPath.row];
if (indexPath.row == self.bestOne)
{
cell.detailTextLabel.text = #"Marked";
cell.textLabel.textColor = [UIColor redColor];
}
else
{
cell.detailTextLabel.text = #"";
}
On reuse, you get the exact cell that you had set before. While other cells are undistinguishable, the marked cell stands out with its specifically set detailTextLabel. You need to reset it before you can use it as an unmarked cell.
you look like you are on the right track with the color issue, you may accumulate red colored text cells, if you aren't calling [tableView reloadData] or reloading the old red cells specifically, which you would want to do if you have a large table. your scrolling looks good, don't know why that wouldn't work.

Problem with configuring a UITableView as a inclusive selection list

I am programing a UITableView to behave as an inclusive selection list. My table displays correctly and allows for multiple cells to be selected with check boxes. My problem is that cells which have been selected (cells contain a check mark to the right) loose their selected status when scrolled out of view (cells check mark disappears). I want the selections made to cells in the table to be preserved even if cells are scrolled out of view. Does anyone have any idea what is causing this?
Here is my code inside of my TableViewController class:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [widgetTitles_glob objectAtIndex:row];
cell.detailTextLabel.text = #"";
cell.textLabel.textColor = [UIColor blackColor];
cell.textLabel.font = [UIFont boldSystemFontOfSize:15];
cell.accessoryType = UITableViewCellAccessoryNone;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:[tableView indexPathForSelectedRow] animated:YES];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
// Reflect selection in data model
} else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
// Reflect deselection in data model
}
}
Any help would be very much appreciated.
When you are using UITableView correctly, only as many UITableViewCell instances are allocated as are needed to fit on the screen. When you scroll down a table, and a cell disappears off the top of the screen, it is relocated to the bottom.
Your delegate method, tableView:cellForRowAtIndexPath: is responsible for setting up a cell, either creating a new one or reconfiguring a recycled one.
The proper thing to do is use an array to store your checked/unchecked values. When didSelectRowAtIndexPath: is called, you update the cell and your array. When tableView:cellForRowAtIndexPath: is called, you configure the cell based on the values in the array.
Based on your comments, you are already doing the right thing in didSelectRowAtIndexPath:; you just need to use those values when you set up the cell instance, because that cell could represent a row that has already been checked. Check the array and then set cell.accessoryType accordingly.
In cellForRowAtIndexPath: you were assigining the accessoryType as none, so whenever you scroll that delegate is called and set the accessory type as none. So you should change your code.
I have also faced this problem once; I came up with a solution as follows.
Store the indexPath.row values of selected indexPath in an array (this code should be in didSelectRowAtIndexPath delegate) if it is deselected remove from that array. In cellForRowAtIndexPath: method I have used a for loop and check if that indexPath.row is present then change it's accessory type to checkmark else none.
Thanks for your help. It actually turns out that the reason why the cells were getting reset to UITableViewCellAccessoryNone was becasue of the following line of code inside of cellForRowAtindexPath:
cell.accessoryType = UITableViewCellAccessoryNone;
Removing this has fixed the table.