views:

60

answers:

4

I have a UItableview with cells. Some cells have uilabels and some have uibuttons. The UIbuttons are created whenever the first character in an array is "^". However, the uibuttons repeat when i scroll down (appearing over the uilabel).. and then multiply over the uilabels when I scroll up. Any clues why?

+1  A: 

The content of a cell should not be set inside the if (cell == nil). Only the structure (layout) should be defined. The content should be set after the check:

CellIdentifier = @"TableCell";

cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

        // setup everything that's the same for every cell
        // (e.g. add controls to the cell)
}

// setup everything that's different in each cell (set text, images, ...)

What's happening here is that the cell is being "prepared" and cached, so it won't have to be recreated for every cell. If you set the content of the cell inside the if (cell == nil), you're storing the contents of the cell in the cache.

Philippe Leybaert
I'm not sure what the difference between structure and content is, in the context of my example.
cannyboy
"structure" is everything that's the same for all cells. "content" is what changes in every cell.
Philippe Leybaert
Sorry, I still can't figure this out in the context of my example.
cannyboy
+1  A: 

Actaully every time the section is scrolled and rows are loaded, all the labels and ui components are added to the cell at run time, you need to clear all the views which are added in the cell, everytime when your cell is dequeued and is not nil, See the following example:

    static NSString *CellIdentifier;

    CellIdentifier = [NSString stringWithFormat:@"Cell%d%d",indexPath.section,indexPath.row];

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
/*
 *  THIS IS FOR OS VERSION 2.2 THE OLDER VERSION
 *        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
 */

    } else {
        for (UIView* tempView in cell.contentView.subviews) {
            [tempView removeFromSuperview];
        }
    }
Ansari
I used iAnsari's method and put in the label and button creation code after the tempView removeFromSuperview loop and it seems to work. Possibly not the 'best' way.
cannyboy
+1  A: 

UITableView does not retain one UITableViewCell for each logical row. It only retains the number of cells immediately necessary to display the rows currently on screen. In practice this means that only 9-11 default height cells exist at any one time.

The table view creates the illusion of an arbitrarily long table by calling the table view's datasource and requesting either a new cell for the row or to reuse an existing cell. This reuse is called "dequeueing" the cell. After you've scrolled past the intial displayed rows, you no longer create new cell instances but instead use the same ones over and over again.

Your problem arises because once you set a cell to have UIButton, you never subsequently remove the button. Once a cell has a button in it, it will display that button forever even when it is used to display a logical row that should not have a button. Because the same cells are used over and over again to display different rows, scrolling rapidly causes all the cells to show buttons.

To correct your problem you need to remove the button when you want to display a label and remove the label when you want to display a button.

Better yet, you should define two subclasses of UITableViewCell, one for each display. When you have a row with a button return the button subclass cell and when you have one with a label return the label subclass. That way you can move all the cell configuration logic to the cell class and only set the data in the datasource methods. Using subclasses to encapsulate custom logic results in much neater and cleaner code that is easier to debug and maintain.

TechZen
I followed iAnsari's method, and created the cell items (button or label) after removing from superView. I think your solution would be much neater, but I find it difficult to get my head around it. I will return to the problem when I have more time.
cannyboy
You should hit the checkmark next to iAnsari's answer then.
TechZen
A: 

You are using reusable cells, so in cellForRowAtIndexPath: you have to check - is there a button or label in cell? If so, remove them, alloc-init a new one and add it as subview. To do that use tags, it is very comfortable.

beefon