My UITable is re-using cells when it shouldn't! - iphone

I have a UITableViewController which has two sections. The first section shows a single cell with centered text, saying Add a new Slide. The second section show the current slides.
When a user taps on the Add a new slide cell, a new UITableVeiwController is pushed onto the stack that shows an editor. If the user saves the new slide (by tapping save), the cell is added to the data source and the editor is popped from the stack.
I have two problems:
When the editor is popped, if a cell was deleted before Add a new slide was tapped, the old cell shows up instead of the new one. Popping the UITableViewController (by tapping the automatically generated back button) fixes this, but I'd like this to not happen at all. (Originally, popping the table did not update after popping the editor, so I added [self.tableView reloadData]; to the viewDidAppear method.)
After a certain number of slides, the last slide on the list becomes the Add a new slide cell. I know that the data is being entered properly because another part of the app, which uses the same data source, updates correctly. The table supports editing in the second section, and when you swap the order of the cells, it behaves correctly behind the scenes, but the wrong cell is still there.
What could be going on?
Here's some of my code:
Note that as I was gearing to post my code, I noticed a mismatch of the braces. The check for cell==nil seems to encompass the second part of the code which determines the content of the cells. This fixes the label of the cells in the second section of the table, but the style is still wrong. I've since fixed the code, but the original is posted here.
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
if ([indexPath section] == 0 ) {
cell = [[[MBTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}else if([indexPath section] == 1){
cell = [[[MBTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
if ([indexPath section] == 0) {
[cell.textLabel setTextAlignment:UITextAlignmentCenter];
[cell.textLabel setText:#"Add a New Slide"];
}else if([indexPath section] == 1){
NSArray *storedViewsInfo = [[NSArray alloc] initWithArray:[kSettings arrayForKey:#"views"]];
if ([[[storedViewsInfo objectAtIndex:[indexPath row]] valueForKey:#"type"] isEqualToString:#"announcement"]) {
[cell.detailTextLabel setText:#"Custom Announcement"];
[cell.textLabel setText:[[[storedViewsInfo objectAtIndex:[indexPath row]] valueForKey:#"info"] valueForKey:#"text"]];
}
[storedViewsInfo release];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
}
}
return cell;
}

Without seeing the code, first thing that comes to mind is checking if you've given your custom cells different identifiers in your - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; method?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier1 = #"CellIdentifier1";
static NSString *Cellidentifier2 = #"CellIdentifier2";
if (indexPath.section == kAddSlideSection) {
CustomCell *cellType1 = (CustomCell*) [tableView dequeueReusableCellWithIdentifier:CellIdentifier1];
...
} else {
CustomCell *cellType2 = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier2];
...
}
}
Also it might be worth considering implementing a delegate method that gets called when your user finishes adding the new slide - i.e. if successful call [self.tableview reloadData] from that method instead of in viewWillAppear.

Related

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;
}

Add Blank Cell to each Group in UITableView

I have a tableview that has groups. I would like to add a dummy cell at the top of each group, but not add it to the data. I dont want it saved. So if the top cell is clicked, its data passed will be blank and I can handle the passed view differently. I currently am configuring the cells manually.
So if group one had two entries, the display of those two entries would be prepended with a fake entry at what would be index 0
Below is a preview of what I am trying to do.
PS: Can one cell have a different disclosure icon? Does that break Interface Guidelines?
It would be something like this, you are not really adding anything to your model, you are just modifying your view:
- (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];
}
if ([indexPath row] == 0) {
//And any other default behavior
[[cell textLabel] setText:#""];
}
else{
//Your code for each cell
}
return cell;
}
The HIG doesn't mention anything about having different disclosure icons:
iOS includes some table-view elements that can extend the functionality of table views. Unless noted otherwise, these elements are suitable for use with table views only.

UITableViewCell cell not showing up

Is there any method in the UITableView for the tableviewcell when you are sliding the tableview and cells are being hidden or deleted. I have this code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
int curIndex = 0;
for (int i = 0; i < [dataHolder.dateArray count]; i++)
{
if ([[dataHolder.dateArray objectAtIndex:i] isEqual:[dataHolder.allDates objectAtIndex:[indexPath section]]])
{
if ([self indexHasContains:i] == NO)
{
curIndex = i;
[indexHasChossen addObject:[NSString stringWithFormat:#"%i", i]];
break;
}
}
}
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.textLabel.text = [NSString stringWithFormat:#"%#, %#", [dataHolder.courseArray objectAtIndex:curIndex], [dataHolder.placeArray objectAtIndex:curIndex]];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#", [dataHolder.timeArray objectAtIndex:curIndex]];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.textLabel.textColor = [UIColor blackColor];
cell.detailTextLabel.textColor = [UIColor whiteColor];
return cell;
}
I also want a method that delets from the indexHasChossen array when a cell is being hidden/deleted. I have looked through the apple dokumentation and haven’t find anything yet. Do any one know any way to do this?
It doesn't matter how tableView:cellForRowAtIndexPath: if you want to hide or delete cells. The table view calls this method only when it knows cells exist. It depends on what you return in the methods numberOfSectionsInTableView: and tableView:numberOfRowsInSection: method. Most of the times the former returns 1 so if you want to eliminate an entire section than you should've some kind of marker such as sectionHidden which is boolean value indicating whether section is hidden or not.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if ( sectionHidden )
return 0;
else
return 1;
}
and wherever you want to initiate the delete action do something like this,
sectionHidden = YES;
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0]
withRowAnimation:UITableViewRowAnimationFade];
and to flip it back on do sectionHidden = NO and call reloadSections:withRowAnimation:.
Same thing applies for rows, where you will have to alter the tableView:numberOfRowsInSection: method to reflect that you've deleted the rows or hidden the rows. This time you've to use reloadRowsAtIndexPaths:withRowAnimation: instead of reloadSections:withRowAnimation: method.
Although there's no standard concept of "hidden" tableViewCells, cells deleted by the user are reported in tableView:commitEditingStyle:forRowAtIndexPath:
But let me also add that you seem to be tracking "hasChosen" in cellForRowAtIndexPath . This method only means that the cell is about to appear on screen, not that it's been chosen. "Chosen" occurs when your delegate is called with tableView:didSelectRowAtIndexPath:
Edit: ah, maybe by "hidden", you mean that it's gone off-screen. No, I don't believe there is such a call, (although you could cheat a bit and look at any dequeued cells you get, as those are cells that were formerly on-screen and are now available).
Deleting cells from uitableview is easy. I recommend taking a look on the iPhoneCoreDataRecipes project from the apple developer docs.
You will have to add a function called commitEditingStyle to your UITableViewDelegate, and add the edit button (self.editButtonItem in UITableViewController) to allow editing mode.
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the object for the given index path
}
}

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.