views:

185

answers:

3

Hi,

I have an heterogeneous tableView with an entire section made up of UITableViewCells with a UITextField control on each one's contetView. The problem is that I see text written in textfields being mixed when I scroll.

I've seen many times before this problem and though it's deprecated I solved by assigning a different cellIdentifier for every kind of cell.

What's different now is that I'm creating cells dynamically and depending on the answer of the server the section of the table might have zero or more custom cells (the one that has the textField).

Now, it's interesting to see that textfiled's properties like the placeholder is persistent throughout the scrolls but the text I write is not persistent at all, the text changes it's position...

I have a video here so you can see what I mean.

The code I use is like the following:

    // Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";
    NSString *varCellID = [NSString stringWithFormat:@"%i_VarCellID", (indexPath.row + 7891)];

    if (indexPath.section == 0) {
           UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

        if (cell == nil) {
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
            [self configureCellViews:cell indexPath:indexPath];
        }
        [self configureCellContent:cell indexPath:indexPath];
        return cell;
    }
    else if (indexPath.section == 1) {
              UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

        if (cell == nil) {
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
            [self configureCellViews:cell indexPath:indexPath];
        }
        [self configureCellContent:cell indexPath:indexPath];
        return cell;
    }
    else {
            UITableViewCell *varCell = [tableView dequeueReusableCellWithIdentifier:varCellID];

        if (varCell == nil) {
            varCell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:varCellID] autorelease];
            varCell.selectionStyle = UITableViewCellSelectionStyleNone;
            [self configureCellViews:varCell indexPath:indexPath];
        }
        [self configureCellContent:varCell indexPath:indexPath];
        return varCell;
    }
}

I know it's not elegant but woked before. Except for the dinamically created cell identifier that is one of my (desperate) attempts to keep individual contents on each textField.

Here's how I create cell's views and content:

#pragma mark -
#pragma mark  ConfigureCellcontent

- (void)configureCellViews:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath {

    NSArray *aSection = [sections objectAtIndex:indexPath.section];
    NSDictionary *aField;
    int varformatid;

    if (indexPath.section == 0) {

        cell.detailTextLabel.textAlignment = UITextAlignmentLeft;
    }
    else if (indexPath.section == 1) {

        cell.detailTextLabel.textAlignment = UITextAlignmentLeft;
    }
    else {

        aField = [aSection objectAtIndex:indexPath.row];
        varformatid = [[aField valueForKey:@"varformatid"] intValue];
        NSArray *aSection = [sections objectAtIndex:indexPath.section];
        NSLog(@"format id %@", [[aSection objectAtIndex:0] valueForKey:@"varname"]);

        fieldLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 7, 100, 30)];
        fieldLabel.tag = 1234;
        fieldLabel.textAlignment = UITextAlignmentLeft;
        fieldLabel.font = [UIFont boldSystemFontOfSize:14];
        fieldLabel.adjustsFontSizeToFitWidth = YES;
        [cell.contentView addSubview:fieldLabel];

        theTextField = [[UITextField alloc] initWithFrame:CGRectMake(130, 12, 170, 30)];
        theTextField.textColor = kAnswersTextColor;
        theTextField.returnKeyType = UIReturnKeyDone;
        theTextField.tag = indexPath.row + 101;
        theTextField.adjustsFontSizeToFitWidth = YES;
        theTextField.delegate = self;


        if(varformatid == 6 || varformatid == 7 || varformatid == 8) {
            // use number pad input
            theTextField.keyboardType = UIKeyboardTypeNumberPad;
            [cell addSubview: theTextField];
            [theTextField release];
        }
        else if(varformatid == 4) {
            // use date
        }
        else if(varformatid == 17 || varformatid == 18) {
            // use pull down, elements of pull down are in XML
        }
        else if(varformatid == 20) {
            theSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(120, 8, 100, 30)];
            theSwitch.selected = YES;
            [cell.contentView addSubview:theSwitch];
        }
        else {
            // use input string
            [cell addSubview: theTextField];
            [theTextField release];
        }       
    }

}

- (void)configureCellContent:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath {

    NSArray *aSection = [sections objectAtIndex:indexPath.section];
    NSString *aField;


    if (indexPath.section == 0) {
        aField = [[aSection objectAtIndex:0] valueForKey:@"SetDate"];
        cell.textLabel.text = @"Set Date";
        cell.detailTextLabel.text = aField;
    }
    else if (indexPath.section == 1) {
        aField = [[aSection objectAtIndex:0] valueForKey:@"SetType"];
        cell.textLabel.text = @"Set Type";
        cell.detailTextLabel.text = aField;
    }

    else {

        aField = [[aSection objectAtIndex:indexPath.row] valueForKey:@"varname"];
        if ([[[aSection objectAtIndex:indexPath.row] valueForKey:@"varrequired"] isEqualToString:@"yes"]) {
            [(UITextField *)[cell viewWithTag:indexPath.row + 101] setPlaceholder: @"Required"];
        }
        else [(UITextField *)[cell viewWithTag:indexPath.row + 101] setPlaceholder: @"Optional"];
        [(UILabel *)[cell.contentView viewWithTag:1234] setText: aField];

    }
}

Finally I ask your help to find at least one better approach. Thanks in advance.

+1  A: 

Your configureCellContent seems broken. So you either didn't update your internal data structure with the typed text (you probably want to do that on every text change), or you miss to fill in the correct values in configureCellViews.

Sidenote: You shouldn't dequeue two cells at the top. Just dequeue the right one! I.e. put that into the if sections.

Eiko
Thanks, that helped me, now I have more control on text being entered. I have now an array of the same number of sections and rows to store the text every time the textFieldDidEndEditing: method is called for each textField. Though, still I can see content being mixed and it happens only with between the first and the last row.
iPhoneDevProf
Here's the behavior I have now http://www.youtube.com/watch?v=kfmcJrFNPyQ
iPhoneDevProf
+1  A: 

I've encountered this before. I believe it's because UITableViewCells are released when not visible to reducing memory consumption. When you scroll back up, you'll get a newly dequeued instance for that cell.

You'll need to save the state of the cell somewhere to re-initialise it when it returns into view. When you save is entirely up to you - when your UITextField loses focus, or when the field is changed in any way.

jasonmadigan
Good point, actually I solved the problem by creating a mirror array that will contain all strings from the textfields, and every time I create the new cells the method checks if there's content inside the corresponding array's index to put it in the textfield...
iPhoneDevProf