Question

I have a uiview consisting of a map view on top, and a uitableview below. the table view has cells with uitextFields in them with placeholders.

When I edit the fields the placeholder disappears correctly, but if I scroll the uitableviewcells out of sight (i.e. "behind" the map view that sits above the table view), the placeholders reappear together with the content that was written into the uitextfield.

If I replace the placeholders with normal text, and clear the text on textFieldDidBeginEditing, the same ghosting effect happens. Both the placeholder text and the input shows.

I have tried setting placeholders and text to nil in textFieldDidBeginEditing, but to no avail.

The table view uses michaeltyson's TPKeyboardAvoidingTableView.

Ghost placeholder

edit Added my cellForRowAtIndex:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[NSString stringWithFormat:@"Cell%d", indexPath.row ]];
cell.textLabel.text = _tableData[indexPath.row];
if (indexPath.row == 0) {
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
    textField.delegate = self;
    textField.placeholder = @"(Required)";
    [cell.contentView addSubview:textField];
} else if (indexPath.row == 1) {
    UILabel *textField = [[UILabel alloc] initWithFrame:CGRectMake(110, 6, 185, 30)];
    textField.text = @"(Required)";
    textField.textColor = [UIColor lightGrayColor];
    textField.backgroundColor = [UIColor clearColor];
    [cell.contentView addSubview:textField];
    secondCellField = textField;
    secondCell = cell;
} else if (indexPath.row == 2) {
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
    textField.delegate = self;
    textField.placeholder = @"(Optional)";
    [cell.contentView addSubview:textField];
} else if (indexPath.row == 3) {
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
    textField.delegate = self;
    textField.placeholder = @"(Optional)";
    [cell.contentView addSubview:textField];
} else if (indexPath.row == 4) {
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
    textField.delegate = self;
    textField.placeholder = @"(Optional)";
    [cell.contentView addSubview:textField];
} else if (indexPath.row == 5) {
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
    textField.delegate = self;
    textField.placeholder = @"(Optional)";
    [cell.contentView addSubview:textField];
} else if (indexPath.row == 6) {
    UISwitch *textField = [[UISwitch alloc] initWithFrame:CGRectMake(210, 8, 50, 30)];
    [textField addTarget:self action:@selector(segwayToWork) forControlEvents:UIControlEventValueChanged];
    [cell.contentView addSubview:textField];

    _workSwitch = textField;
}

return cell;
Was it helpful?

Solution 3

The problem here is that everytime a cell gets reloaded, you initialize a new text view which won't have the text that was previously entered. The best solution to this problem is for you to keep an NSMutableArray* that stores the user's input. You have different options, i'll just describe the steps for one possible solution:

  • declare an NSMutableArray property for storing the user inputs as follows:
@property (strong, nonatomic)  NSMutableArray *userData;

and the getter that performs lazy initialisation:

 -(NSMutableArray *)userData
 {
    if(!_userData){
        _userData = [[NSMutableArray alloc] initWithCapacity:[self.tableData count]];
        for (int i = 0; i < [self.tableData count]; i++) 
            [_userData addObject:@""];
    }
    return _userData;
 }
  • Use the textfield's property tag to store the row for the particular textfield as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[NSString stringWithFormat:@"Cell%d", indexPath.row ]];
    if(cell == nil)
        cell = [[UITableViewCell alloc] init];

    cell.textLabel.text = _tableData[indexPath.row];        
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
    textField.delegate = self;
    textField.tag = indexPath.row;

    if (indexPath.row == 0) {
        textField.placeholder = @"(Required)";
        [cell.contentView addSubview:textField];
    } else if (indexPath.row == 1) {
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(110, 6, 185, 30)];
        label.text = @"(Required)";
        label.textColor = [UIColor lightGrayColor];
        label.backgroundColor = [UIColor clearColor];
        [cell.contentView addSubview:label];
        secondCellField = label;
        secondCell = cell;
    } else if (indexPath.row == 2) {
        textField.placeholder = @"(Optional)";
        [cell.contentView addSubview:textField];
    } else if (indexPath.row == 3) {
        textField.placeholder = @"(Optional)";
        [cell.contentView addSubview:textField];
    } else if (indexPath.row == 4) {
        textField.placeholder = @"(Optional)";
        [cell.contentView addSubview:textField];
    } else if (indexPath.row == 5) {
        textField.placeholder = @"(Optional)";
        [cell.contentView addSubview:textField];
    } else if (indexPath.row == 6) {
        UISwitch *switch = [[UISwitch alloc] initWithFrame:CGRectMake(210, 8, 50, 30)];
        [switch addTarget:self action:@selector(segwayToWork) forControlEvents:UIControlEventValueChanged];
        [cell.contentView addSubview:switch];

        _workSwitch = switch;
    }


    if(![[self.userData objectAtIndex:indexPath.row] isEqualToString:@""]){
        NSLog(@"%@ at indexPath.row %d",[self.userData objectAtIndex:indexPath.row], indexPath.row);
        textField.placeholder = nil;
        textField.text = [self.userData objectAtIndex:indexPath.row];
    }
    return cell;
}
  • Finally, in your UITextField delegate, you can do something like:
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    self.userData[textField.tag] = textField.text;
    return YES;
}

Now when a cell will be reloaded, it will first check whether there was already a user input, and if there was one, it will set the placeholder to nil and will instead show just the user input.

OTHER TIPS

Your problem is mutiple TextFields are created for different cell. You need to use reusable cells Like:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (cell == nil) {
        // allocate the cell:
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
         if (indexPath.row == 0) {
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
    textField.delegate = self;
    textField.tag = 1111;//tag
    textField.placeholder = @"(Required)";
    [cell.contentView addSubview:textField];
} else if (indexPath.row == 1) {
    UILabel *textField = [[UILabel alloc] initWithFrame:CGRectMake(110, 6, 185, 30)];
    textField.text = @"(Required)";
    textField.tag = 1111;//tag
    textField.textColor = [UIColor lightGrayColor];
    textField.backgroundColor = [UIColor clearColor];
    [cell.contentView addSubview:textField];
    secondCellField = textField;
    secondCell = cell;
} else if (indexPath.row == 2) {
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
    textField.delegate = self;
    textField.tag = 1111;//tag
    textField.placeholder = @"(Optional)";
    [cell.contentView addSubview:textField];
} else if (indexPath.row == 3) {
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
    textField.delegate = self;
    textField.tag = 1111;//tag
    textField.placeholder = @"(Optional)";
    [cell.contentView addSubview:textField];
} else if (indexPath.row == 4) {
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
    textField.delegate = self;
    textField.tag = 1111;//tag
    textField.placeholder = @"(Optional)";
    [cell.contentView addSubview:textField];
} else if (indexPath.row == 5) {
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
    textField.tag = 1111;//tag
    textField.delegate = self;
    textField.placeholder = @"(Optional)";
    [cell.contentView addSubview:textField];
} elseif (indexPath.row == 6) {
    UISwitch *textField = [[UISwitch alloc] initWithFrame:CGRectMake(210, 8, 50, 30)];
    textField.tag = 1111;//tag
    [textField addTarget:self action:@selector(segwayToWork) forControlEvents:UIControlEventValueChanged];
    [cell.contentView addSubview:textField];

    _workSwitch = textField;
}

}
cell.textLabel.text = _tableData[indexPath.row];

return cell;

}

I have tryied to reuse UITableViewCell So please customize it as per your requirement. you can use this code also.

Creating new text fields is indeed the problem. You should NOT alloc/init any text fields in cellForRowAtIndexPath.

Instead, either make a custom cell or use the "dynamic prototype" in your storyboard file. Give your text field a tag so you can easily configure it.

//above
#define TextFieldTag 42

// in cellForRowAtIndexPath:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
// If @"Cell" is the identifier you specified
// if you have storyboard or a xib, no need to check for cell==nil

UITextField *textField = (UITextField*) [cell viewWithTag:TextFieldTag]; 
// configure the text field based on indexPath.row

In case you are not using storyboard or xibs, you can lazily instantiate the text field like this:

UITextField *textField = (UITextField*) [cell viewWithTag:TextFieldTag];
if (!textField) {
   textField = [[UITextField alloc] initWithFrame:textFieldFrame];
   textField.tag = TextFieldTag; 
   // more standard configuration
   [cell.contentView addSubview:textField];
}

And by the way, here is another tip for you. You should not hard code your table view rows. This results in not very readable code. You can use a simple enum on top of your class:

typedef enum {
   NameCell, AddressCell, 
   FirstOptionalCell, SecondOptionalCell,
   ThirdOptionalCell, CellCount } TableCell;

You can then just return "CellCount" in numberOrRowsInSection and reference the rows like this:

if (indexPath.row == NameCell) 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top