Steaphann
Steaphann

Reputation: 2777

tableviews cells are changing after scrolling down

I am making a form within a grouped tableview. In this form I have UIswitches and textfields. But after scrolling down, the cells styles are changing.

Here is my cellForRowAtIndex

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = nil;
    static NSString *MyIdentifier = @"GenericCell";
    cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] ;
    }
        NSString *text = nil;

    if(indexPath.section == CREDENTIALS_SECTION){
        if (indexPath.row == 0) {
            NSLog(@"tot hier login");
            UITextField *login = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
            login.adjustsFontSizeToFitWidth = YES;
            login.placeholder = @"[email protected]";
            login.keyboardType = UIKeyboardTypeEmailAddress;
            login.returnKeyType = UIReturnKeyNext;
            login.backgroundColor = [UIColor clearColor];
            login.tag = 0;
            login.delegate = self;

            [login setEnabled: YES];

            [cell addSubview:login];
        }else if (indexPath.row == 1){
            NSLog(@"tot hier pass");
            UITextField *pass = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
            pass.adjustsFontSizeToFitWidth = YES;
            pass.placeholder = @"Required";
            pass.keyboardType = UIKeyboardTypeDefault;
            pass.returnKeyType = UIReturnKeyDone;
            pass.secureTextEntry = YES;

            pass.backgroundColor = [UIColor clearColor];
            pass.tag = 0;
            pass.delegate = self;
            [cell addSubview:pass];
        }

        if (indexPath.row == 0) { // Email
            text = @"Email";
        }
        else if(indexPath.row == 1) {
            text = @"Password";
        }
    }else  if(indexPath.section == METHODS_SECTION){
        UISwitch *toggleSwitch = [[UISwitch alloc]initWithFrame:CGRectMake(220, 10, 100, 30)];
        toggleSwitch.tag = indexPath.row;
        [toggleSwitch addTarget:self action:@selector(toggleSwitched:) forControlEvents:UIControlEventValueChanged];
        [cell addSubview:toggleSwitch];

        if (indexPath.row == 0) { // Web
            text = @"Web applicatie";
        }
        else if(indexPath.row == 1) { //Mobile
            text = @"Mobiele applicatie";
        }
        else if(indexPath.row == 2) { //Mail
            text = @"E-mail";
        }


    }else  if(indexPath.section == PHONE_SECTION){
        UITextField *phoneText = [[UITextField alloc] initWithFrame:CGRectMake(20, 10, 185, 30)];
        phoneText.adjustsFontSizeToFitWidth = YES;
        phoneText.font = [UIFont fontWithName:@"Arial-BoldMT" size:18];
        phoneText.keyboardType = UIKeyboardTypeNumberPad;
        phoneText.delegate = self;
        phoneText.textColor = [UIColor blackColor];
        phoneText.text = _person.phone;
        [cell addSubview:phoneText];


    }else  if(indexPath.section == REMARK_SECTION){
        UITextView *textView = [[UITextView alloc]initWithFrame:CGRectMake(20, 10, 280, 260)];
        textView.text = _person.remark;
        textView.delegate = self;
        textView.font = [UIFont fontWithName:@"Arial" size:15.0];
        textView.backgroundColor = [UIColor clearColor];

        [cell addSubview:textView];
        text = @"";


    }else  if(indexPath.section == BUTTON_SECTION){
        cell.backgroundColor = [UIColor redColor];
        text = @"test";

    }
    cell.textLabel.text = text;
    return cell;
}

After some searching I found that more people are having this problem. And that the problem lays in this piece of code.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = nil;
    static NSString *MyIdentifier = @"GenericCell";
    cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] ;
    }
        NSString *text = nil;

But I don't find a solution for it. Hope anybody can help!

Kind regards!

Clarification

Oké so here you see a screenshot of my form. below I have a red cell (save button) when I scroll down other cells are getting a red background. And some cells, text property's are changing. enter image description here

Upvotes: 4

Views: 6454

Answers (6)

Hermann Klecker
Hermann Klecker

Reputation: 14068

That is not gong to work. Aparently you did not yet fully understand how the re-use mechanism works.

What do you do? First you fetch a cell to be re-used. If you get one -fine so far but the problem comes later. If you don't get one then you create a new one.

When you have created a new one, which is the case at start before the user begins scrolling, then you add some UIItems depending on section and row. I will explain why this is not actually a smart thing to do.

Then the user scrolls. Cells will dissappear from screen and then made available for re-use. Then you will fetch the cells for re-use. But it may well happen that those cells already have additional UI-Items on them because you have used them before in that way. In the following process you will add new UI Items regardless whether there are already additional UI-Items on that very cell.

What can you do:

  1. Create your own custom table cell subclasses. One subclass for each set of additional ui items that you may need. That is probably the neatest way of doing it. For each subclass use a different re-use identifier (!!!) This is what I would recommend! However, there are alternatives:

  2. You could still live with your concept but invent an individual type of re-use identfier for each type of cell that has some type of additional ui item on it. If so, then make sure that these UI items are only created and added as sub-views in the if (cell == nil) branch of your code. Only create them once and then re-use them. Cell reuse-IDs could be "email-display", "email-input" , "password-display", "password-input", "switch", ...

  3. A variance of the solution above would be, to calculate row and section into the reuse-identifier. Such as "cell-id-0.2" for section 0 and row 2 - or so. But still you will have to make sure that you really re-use the additional UI views and do not re-create them every time when the cell is filled with data. Plus, the layout in your first section varies depending on whether you want to input password and e-mail or just display them. You will still have to deal with those variations.

  4. If cell == nil - meaning if a cell is re-used - then first clean it from every UI item that you may have added before. You can do that by tagging your UIViews with - let's say 99 - (anything different from 0 should do) upon creation and when reusing enumerate over all subviews and remove those, which have the tag 99. Despite that you can stick with the code that you have already made.

Upvotes: 3

Mert
Mert

Reputation: 6065

Actually you have some bad code here.

In the mehthod

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

Unless it is not in if (cell == nil), you should NOT initialize and use any

-(void)addSubview:(UIView*)view 

Why?

The cells are views which are reused from tableview. So If you add some subview, next time while reusing the cell, it will be added more subviews on it. Simply they are overlapped and may cause MEMORY LEAK.

Do not forget that cells are reusable. So;

if I have the following code unless I do not set text somewhere else. It is expected to all cells has the text in their text labels "this is a text". Because they are reusable.

if (someChangingBool) {
   cell.textLabel.text = @"this is a text"; 
}

So I need to have an else for that if which sets the text something else.

For more Information.

Upvotes: 0

Roland Keesom
Roland Keesom

Reputation: 8288

The easiest fix is:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"GenericCell"] ;
    //some more code
    return cell;
}

This would remove the reusability from the tableview, but since it's a limited settings view, it can be ok. I would still advice taking 1 or 2 from Hermann Klecker's solutions.

Upvotes: 2

iPhoneDeveloper
iPhoneDeveloper

Reputation: 179

If you also need to persist UIControl state then use

static NSString *MyIdentifier = [NSString stringWithFormat:@"GenericCell%d",indexPath.row];

It will always return your unique table row and you can use it as required.

Upvotes: 1

Girish
Girish

Reputation: 4712

Remove all subviews before adding the subviews on cell.

if (cell == nil)
{
      cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault    reuseIdentifier:SimpleTableIdentifier]autorelease];
}
else 
{
    //To remove the subview of cell.
    for (UIView *vwSubviews in [cell.contentView subviews])
    {
            [vwSubviews removeFromSuperview];
    }
}

It may solves your problem.

Upvotes: 0

Anusha Kottiyal
Anusha Kottiyal

Reputation: 3905

Try to remove all subviews from cell before reusing it. Try the code :

if (cell == nil) 
{
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] ;
}
else
{
   [cell.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
}

Upvotes: 0

Related Questions