UITableViewCell cell not showing up - iphone

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

Related

What is wrong with my UITableView cellForRowAtIndex for Single Selection?

Below is code for UITableView, But when i scroll its behaves weirdly (too annoying)... This problem is due to reuseIdentifier.... but dont know how to solve..
- (UITableViewCell *)tableView:(UITableView *)tableView1 cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView1 dequeueReusableCellWithIdentifier:CellIdentifier];
NSInteger imgTag = 1;
NSInteger lblTag = 2;
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(2, 2, 52, 52)];
// Image:[UIImage imageNamed:[self.glassType objectAtIndex:indexPath.row]]];
imgView.tag = imgTag;
[cell.contentView addSubview:imgView];
[imgView release];
UILabel *lblName = [[UILabel alloc] initWithFrame:CGRectMake(60, cell.frame.size.height/4, 200, 21)];
// lblName.text = [self.glassName objectAtIndex:indexPath.row];
lblName.tag = lblTag;
[cell addSubview:lblName];
[lblName release];
}
NSInteger imgIndex = 2;
NSInteger lblIndex = 3;
((UIImageView *)[cell viewWithTag:imgTag]).image = [[self.glassType objectAtIndex:indexPath.row] objectAtIndex:imgIndex];
((UILabel *)[cell viewWithTag:lblTag]).text = [[self.glassName objectAtIndex:indexPath.row] objectAtIndex:lblIndex];
return cell;
}
- (void)tableView:(UITableView *)tableView1 didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView1 cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
How to make Cell for row at index so that it remains constant even when scrolled??? Also how to make single selection in UITableView??
The answer is that you should not add subviews to your table cells outside of the "if (cell == nil) { ..." clause or they get added over and over again to the same cell when it gets re-used.
See my answer to this question for a more detailed explanation, including code for how to fix it:
cellForRowAtIndexPath memory management
You also cannot store state in table cells because as soon as they scroll offscreen they are recycled and re-appear at a different index in your table. You need to set up an array of model objects to store state for your table cells (such as what their accessory type should be). A more detailed explanation can be found in my answer to this question:
Looping through UITableViewCells of a UITableView
If you fix how you are adding subviews to the cells, and store your "ticked" state in an array of model objects as well as setting the cell.accessoryType (so that it can be restored when the cell is dequeued), then your approach to row selection is otherwise correct.
So put this in your tableView:cellForRowAtIndexPath: method, just before the return cell;:
MyModelObject *object = [self.arrayOfModelObjects objectAtIndex:indexPath.row];
BOOL isChecked = object.checked;
cell.accessoryType = isChecked? UITableViewCellAccessoryCheckmark: UITableViewCellAccessoryNone;
And in your tableView: didSelectRowAtIndexPath: method, get rid of the current logic and replace it with:
- (void)tableView:(UITableView *)tableView1 didSelectRowAtIndexPath:(NSIndexPath *)indexPath
for (int i = 0; i < [self.arrayOfModelObjects count]; i++)
{
MyModelObject *object = [self.arrayOfModelObjects objectAtIndex:i];
object.checked = (i == indexPath.row); // only check the one we just tapped
}
//refresh table to update the accessory views for all rows
[tableView1 reloadData];
}
Obviously replace the arrayOfModelObjects with your own model implementation. You could just use an array of NSNumber objects containing bools if you don't want to create a custom class for this purpose.
The recycling queue is like a pool where previously created Cells are stored before to reuse them. For example when you scrolls up, at the moment the cell disappears above, it is stored in the queue and becomes available for the cell that will appear at the bottom. Ok ?
Actually the number of cells really created is exactly the max simultaneous cell you can display in your table (in most cases from 3 to 8). In other words your if (cell == nil) code is executed (more or less from 3 to 8 times) at the first reloadData to create the pool of cells your table needs.
Then all you make on a cell is kept as it and appears again when you dequeue it. It's now easy to understand that, in your code, you have to make all strictly row-dependant settings outside the if (cell == nil) block. The same way, do not add subViews outside the if (cell == nil) block, you can imagine the thousands of subview you will add each time you reset a dequeued cell !
Tip: if you need some custom cleanup before reusing a cell (like to set an image to blank), you can create a custom UITableviewCell class and implements the prepareForReuse method.
Is it clear ?
Always reload your tableView in viewWillAppear method instead of viewDidLoad.
(void) viewWillAppear:(BOOL)animated{
[self.tableView reloadData];
}
This avoids most of all unexpected and annoying problems. :)

Problem: Multiple selection tableview cells being reused

I have a tableview, i need to have a checkmark displayed everytime i select a row. (Multiple selection) My code is given below. I am also able to unselect a row.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *RootViewControllerCell = #"RootViewControllerCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:RootViewControllerCell];
if(nil == cell)
{
cell = [[[UITableViewCell alloc]initWithFrame:CGRectZero reuseIdentifier:RootViewControllerCell] autorelease];
}
cell.textLabel.font = [UIFont fontWithName:[NSString stringWithFormat:#"HelveticaNeue-Bold"] size:12];
textView.textColor = [UIColor colorWithRed:0.281 green:0.731 blue:0.8789 alpha:1];
cell.textLabel.text = [optionsArray objectAtIndex:[indexPath row]];
if (pathIndex == indexPath)
{
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
else {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
pathIndex = indexPath;
[surveytableView reloadData];
}
But i have a problem with the cells being reused. When i select a cell another cell elsewhere also gets selected. Only the checkmark (or no checkmark) is getting reused other details like the row title, etc., don't get reused. Any solutions to fix this. Thanks in advance.
Add it to cellForRowAtIndexPath:
if ([tableView.indexPathsForSelectedRows containsObject:indexPath]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else if (![tableView.indexPathsForSelectedRows containsObject:indexPath]) {
cell.accessoryType = UITableViewCellAccessoryNone;
}
Worked for me
At your
if (pathIndex == indexPath)
you're comparing pointers not the values of them, try
[pathIndex isEqual:indexPath]
or use
- (NSComparisonResult)compare:(NSIndexPath *)otherObject;
Next you're assigning a value to pathIndex without retaining or copying it like
pathIndex = [indexPath copy];
(of course now since you're retaining the value, before copying your new object you have to release the previous one [pathIndex release];)
Finally, no multiple selection is provided by your implementation, only single selection. You could try adding and removing NSIndexPath objects to an NSMutableArray and check against their presence in your mutable array at cellForRowAtIndexPath instead.
The issue is that you only do something with the accessory if the current row matches pathIndex.... so what if it is a normal cell...? You never set it back. You want...
cell.accessoryType = UITableViewCellAccessoryNone;
if ([pathIndex isEqual:indexPath])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
It is good practice to reset the cells before you set any specific properties.

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.

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

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.

UITableView Checklist Problem

I have this bizarre problem. I'm making a checklist program with XCode and I'm using UITableView with UITableViewCellAccessoryCheckmark. I can select cells and the checkmark will appear, but somehow, other cells that I have NOT yet selected below will also have a checkmark appear. Any ideas?
Here's my check mark coding:
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
cell = [aTableView cellForRowAtIndexPath: indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
I don't know if this affects it, but I also implemented this method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
// Dequeue or create a cell of the appropriate type.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryNone;
}
// Configure the cell.
if(tableView == Table1){
switch(indexPath.section){
case 0:
cell.textLabel.text = [array1 objectAtIndex:indexPath.row];
break;
case 1:
cell.textLabel.text = [array2 objectAtIndex:indexPath.row];
break;
case 2:
cell.textLabel.text = [array3 objectAtIndex:indexPath.row];
break;
}
//cell.textLabel.text = [NSString stringWithFormat:#"Row %d", indexPath.row];
}
if(tableView == Table2){
cell.textLabel.text = [array4 objectAtIndex:indexPath.row];
}
return cell;
}
Thanks.
Set the cell.accessoryType each time you call tableView:cellForRowAtIndexPath:. Otherwise, when you reuse a cell, you'll get it's accessoryView instead of what you're expecting.
So, yeah, you'll need to keep track in when NSIndexPaths are selected by some method other than just looking at the accessoryType.
You should keep the checked/unchecked info in data source (array).
I also advise you to remove the cell.accessoryType = UITableViewCellAccessoryNone; line.
This line will be executed only for few first cells. All the other cells will be "reused" - meaning that the old (already initiated) cells will be used, and you will have to modify the details that are displayed on these cells (all inside cellForRowAtIndexPath as you do now).
In addition you will have to add a similar line (something like cell.accessoryType = ([[array objectAtIndex:indexPath.row] boolValue] ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;).
In general, I suggest you to use one array for holding the data for your table, when each item in the array will be a dictionary. This way you will be able to hold the texts and the boolean checkmarks (inside NSNumber) and easily access them when needed.