What if no reusable cell in UITableView - iphone

Can anyone please tell me what is the correct way to manage UITableView if I have a big number of cells? The interface of each cell depends on sections (each cell keeps different UI elements in its content view). I do not want to use reusable cells as it messes up with overlapping.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault nil] autorelease];
} else {
cell = nil;
[cell release];
cell = [[[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault nil] autorelease];
}

No, your code is not correct. First of all, it wouldn't even compile because [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault nil] is not valid syntax. Secondly, the [cell release]; line has no effect (which is good because if it had, it would be wrong) but its presence shows that you haven't understood the memory management concepts (yet).
Thirdly and most importantly, you should definitely use the table view's cell reuse, especially if you have a big table. If you have different types of cells, just use different reuse identifiers for them, no problem. The table view will then create multiple reuse pools and always return a cell of the type you ask it for in dequeueReusableCellWithIdentifier:.

I'm using reusable cells in my app. The method I'm using is as follows:
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [yourArray objectAtIndex:row];
return cell;
}
It works fine.

Without reuseIdentfier you will run out of memory fast and tableview will scroll slow. You should change content of cells in this method, e.g. titles, images, etc. but not views. So create subclasses of cell for every section you need. Setup their views and in this method setup content.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
} else {
cell.title = [_cellTitles objectAtIndex:indexPath.row];
cell.image = [_cellImages objectAtIndex:indexPath.section];
}

Related

Problem reuse cell of TableView on iPhone

I have a list of cells in a table view where only the last cell has a disclosure indicator (it triggers an action). When I scroll down my list of cells everything works fine, but if I scroll back up oddly the disclosure indicator appears in other cells too. I can't really figure out where's the problem, any help?
Thanks,
Daniele
This is the part of code that is use:
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [myArray objectAtIndex:indexPath.row];
if(([myArray count]-1) == indexPath.row) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
that is not a bug: it is a feature ;) because the cells are being reused.
If your tableview contains 200 cells and your iphone is able to show 5 cells at the same time then you only have 5-6 instances of UITableViewCell. If you scroll down a cell gets the disclosure-button and if you scroll back the cell is being reused and as a result the disclosure-button is still there.
to solve your problem:
approach 1: not only set the disclosure-button on the last cell. you should also remove/unset it in other cells.
approach 2: it seems that the last cell is another type of cell in semantic. So choose a reuse-identifier for example: #"MyLastCell for the last cell and #"MyCell" for all other cells. As a result your tableview will only reuse cells of the same type (in your case: with/without disclosure-button)
edit 1: some sample-pseudo-code for approach 2 ;
edit 3: shorter solution for approach 2
static NSString *CellIdentifier = #"Cell";
static NSString *LastCellIdentifier = #"LastCell";
bool isLastRow = (indexPath.row == numRows-1);
NSString *usedCellIdentifier = isLastRow ? LastCellIdentifier : CellIdentifier;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: usedCellIdentifier];
if(!cell)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:usedCellIdentifier] autorelease];
if(isLastCell)
{
//do disclosure-stuff here. Or add a UIButton here
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton
}
}
edit 2: sample code for approach 1
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
cell.accessoryType = (idexPath.row == numRows-1) ?
UITableViewCellAccessoryDetailDisclosureButton
: UITableViewCellAccessoryNone ;
As thomas said, you can use his code and you can add this:
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
for the last cell (inside the if where you dequeue your last cell) and this:
cell.accessoryType = UITableViewCellAccessoryNone;
for other cells (inside the if where you dequeue other cells).
I haven't tried this code but it should work... ;)
try this
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [myArray objectAtIndex:indexPath.row];
if(([myArray count]-1) == indexPath.row) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
else{
cell.accessoryType = UITableViewCellAccessoryNone;
}

UITableViewCell turns blue when scrolling

I am getting a really strange error and I can't figure out how to fix it: When I scroll around in my UITableView it will sometimes highlight a cell blue, even when I don't fully selected.
For example:
-Cell 1-
-Cell 2-
-Cell 3-
-Cell 4-
If I put my finger down on Cell 2 and scroll to Cell 4 it will leave Cell 2 highlighted, even though didSelectRowAtIndexPath: is never fired.
Any ideas?
EDIT
Added code:
UITableViewCell *cell = nil;
static NSString *cellId = #"StyleDefault";
cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId] autorelease];
}
cell.textLabel.text = #"Cell One";
cell.textLabel.textAlignment = UITextAlignmentCenter;
[cell setAccessoryView:nil];
return cell;
FIXED IT!
My solution was two parts:
1) In cellForRowAtIndexPath: put "cell.selected = NO;", which fixes the problem of if the cell gets touched down on then goes off screen (scrolling).
UITableViewCell *cell = nil;
static NSString *cellId = #"StyleDefault";
cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId] autorelease];
}
cell.textLabel.text = #"Cell One";
cell.textLabel.textAlignment = UITextAlignmentCenter;
[cell setAccessoryView:nil];
//Here:
cell.selected = NO;
return cell;
2) Put "[tableView deselectRowAtIndexPath:indexPath animated:YES];" into didSelectRowAtIndexPath: instead of what I used to have "[[tableView cellForRowAtIndexPath:indexPath] setSelected:NO];" which was wrong on so many levels.
[tableView deselectRowAtIndexPath:indexPath animated:YES];
Hopefully that helps others that have this issue. Case closed.
You can use following while creating cell.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = Your allocation.
cell.selectionStyle = UITableViewCellSelectionStyleNone;
I guess you need to also please have look at the UIViewTable Property for Touch i.e. Delay Content Touches and Cancellable Content Touches.
Hope this helps.
Override -tableView:willSelectRowAtIndexPath: in your UITableView delegate, and return nil for everything just to make sure you're not allowing it to select the rows.

UITextField inside UITableViewCell gets erased on scroll

I have a small form (5 fields) in a grouped UITableView. Each UITextField is inside of a UITableView cell. When clicking in a textField, it brings up the keyboard, which then allows you to scroll some of the cells out of view, when you pull them back down, their content is erased.
Am I correct in assuming that they have been redrawn, so their content is gone? If so, what is the best way to prevent this? Since I only have 5 fields, do I have to redraw my cells when the are scrolled back into view?
I am reusing cells:
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
You can have a separate array of you text fields and implement you cell creation callback like this:
-(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] autorelease];
}
[[cell.contentView subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)];
[cell.contentView addSubview: [textFieldsArray objectsAtIndex: indexPath.row]];
return cell;
}

Repeated TableViewCells show up when I try to reuse tableviewcells... how do I get the new ones to show?

I am having a problem with my code skipping over the if(cell == nil) after about nine trips through the cellForRowAtIndexPath. Then items in my table start repeating and do so every nine items. When I remove the if(cell == nil) line, the table comes out beautifully, with all the data in the right order. However, if I scroll to the bottom of the table, my app crashes so that is not a good solution. Any ideas please??
Thank you!
- (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] autorelease];
NSString *temp = [[views objectAtIndex:indexPath.row] objectForKey:#"racer"];
NSString *val = [[views objectAtIndex:indexPath.row] objectForKey:#"pointsScored"];
// Set up the cell...
cell.textLabel.text = temp;
cell.textLabel.font = [UIFont boldSystemFontOfSize:15];
cell.detailTextLabel.text = val;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[temp release];
[val release];
}
return cell;
}
KLevSki,
That is because you are re-using tableview cells via dequeueReusableCellWithIdentifier which is a good thing on the iPhone platform. What happens is this:
1) Cell is created in the if (cell==nil) section
2) Once a number of cells are created (in your case 9 of them, based roughly on how many are shown on screen), the OS begins to re-use the table cells to be a good memory manager instead of creating a unique table cell for each and every row which could be memory intensive
3) Since the cell is being re-used, all you need to do in the section after the if (cell==nil) block is to update/change the information on each cell.
As an example... If you created a cell that had only an icon and a label on it, each time the cell is scrolled into view, you would update the icon and label to whatever image/string is appropriate for that cell.
For your case:
...
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
// update cell
cell.textLabel.text = [[views objectAtIndex:indexPath.row] objectForKey:#"racer"];
cell.textLabel.font = [UIFont boldSystemFontOfSize:15];
cell.detailTextLabel.text = [[views objectAtIndex:indexPath.row] objectForKey:#"pointsScored"];
return cell;

Disclosure Indicator disappears?

I have a UITableView which is using custom UITableViewCells to display some information. I'd like to add a UITableViewCellAccessoryDisclosureIndicator to the cells.
Every method that I try successfully adds the chevron, but once the table view scrolls down, they disappear. I'm doing the standard dequeueReusableCellWithIdentifier method, and when I dump out the references to the cells, it's re-using them properly. All of the other data displays fine, it's only the chevrons that disappear.
Things I've tried:
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
accessoryTypeForRowWithIndexPath:
Anyone ever have this happen?
It could happen because you are setting your accessory type outside of the cached cell test:
static NSString *CellIdentifier = #"UIDocumentationCell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[cell setText:mytext];
The better way, IMHO is like this:
static NSString *CellIdentifier = #"UIDocumentationCell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
[cell setText:mytext];
Cocoa Touch doesn't seem to want to modify the cached instance of the cell ( subviews ) setting the disclosure indicator in the cell set method results in the indicator being cached, and redrawn once the cell appears again.