Strange behaviour tableview random cells getting checked

Discussion in 'iOS Programming' started by mraheel, May 25, 2009.

  1. mraheel macrumors regular

    Joined:
    Apr 18, 2009
    #1
    hi,
    A very very strange things going on in my app. First i used the touchCells example from Apple and imported its customCell class into my project. Now Everything worked great!, I then changed the whole datasource to SQL and retrieved about 400 cells worth of data into tableView display.
    Everything still working great. cells are getting "checked" when touched.

    heres the thing,
    While scrolling through the tableview and selecting (checking) cells, I noticed that cells that i selected were unchecked. and some other ones checked.
    I tried rewriting the code and still making things clearer.

    The selection had some kind of a pattern. if Cell 0 is checked, then automatically cell 11 , cell 22, cell 33 got checked too.
    If I scroll down alot faster, this pattern breaks. And some other cells get checked.

    I couldnt really find anything wrong with the code. I thought maybe the indexpath.row got screwd up, but its fine. All values assigned are unique.

    Anyone with Any explanation for this strange behaviour? Tested on simulator.

    An other question I had was,

    How do i retrieve the Indexpath.row of an cell in code. This is obviously easy on touch. But i required to know how to do it programmatically.

    In other words, I have an NSObject, I know that, that Object is displayed somewhere in the tableView. I need to get its location (whats the cell row?) so that I may delete it.
    MutableArray is what stores this data. So I assumed that Index of Object in MutabArray what be the same as the tableView cell row. ( i dont know if i'm correct on this assumption).
    So my code deletion of an a cell goes like.. : get the object, retrieve the cell row of that object in tableview. Cell.row == indexofObj Array. Delete that index from Array, reload tableView.

    Any help would be very very appreciated.
     
  2. mraheel thread starter macrumors regular

    Joined:
    Apr 18, 2009
    #2
    Hmm, a thing i observed : touchcells use pList for storing data including if its checked or unchecked. my app requires things not to be selected at launch. And i edited out all things that would auto update the plist on touch.
    But this doesnt explain this strange behavior? I'll post the code if anyone cares to see it?
     
  3. johnnyjibbs macrumors 68030

    johnnyjibbs

    Joined:
    Sep 18, 2003
    Location:
    London, UK
    #3
    Sounds like a feature of reusing cells.

    In a table view, cells are reused to save memory. In other words, rather than having all the cells from your list (which could be several hundred or thousand), the cell objects get reused so that you only have objects for the visible cells on the screen. What's happening with your 'strange behaviour' is that your first cell is being reused as the 11th cell, as the 22nd, etc.

    The method cellForRowAtIndexPath: gets called every time a cell is created or reused. Basically, if a cell is not available for reuse, it is created. It is here in this method that you can initialise the 'selected' state so that any reused cells are initialised based on your data rather than what state they were in before they were re-used.

    Hope that makes some sense!

    On your second question, the cellForRowAtIndexPath: method passes the index path as the indexPath parameter so you can use indexPath.row to get the current index path. If you are looking at getting the index path of a particular cell, you can use any of the following:

    Code:
    // To get the index paths for the visible cells...
    NSArray *visibleCells = [tableView indexPathsForVisibleCells];
    int iteration;
    for (iteration = 0; iteration < [visibleCells count]; iteration ++) {
        NSIndexPath *indexPath = [visibleCells objectAtIndex:iteration];
        UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
        // put code here
    }
    // etc,etc
    
    // To get the index path for a particular row, e.g.:
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:3 inSection:0];
    // this gets the index path for row 3 (numbered from 0 so actually 4th cell down) in section 0 (the first section)
    
    // To get the indexPath for a partcular point or rectangle co-ord on screen:
    CGPoint indexPoint = CGPointMake(150,200);
    NSIndexPath *indexPath = [tableView indexPathForRowAtPoint:indexPoint];
    
    // or ...
    CGRect indexRect = CGRectMake(150,150,20,20);
    NSArray *indexPaths = [tableView indexPathsForRowsInRect:indexRect];
    // then access the array for each of the index paths of cells that overlap with that...
    
     
  4. mraheel thread starter macrumors regular

    Joined:
    Apr 18, 2009
    #4
    It does make sense :). Yea, i figured that it had something to do with iphone saving memory! The checking of cells (selection) was only a front end process that only added that selected row into an array in my case. The array did not store a BOOL with the data to see if its checked or not. I felt that (in my case) since all the cells will be unchecked by default, and there was no need to save their status, any additional verifying wouldnt be necessary.

    But this reuse makes it necessary!??
    Maybe i should add a new class solely for this storing purpose.

    thanks for your reply
     
  5. mraheel thread starter macrumors regular

    Joined:
    Apr 18, 2009
    #5
    Ah... the solution to te reusing of cells is still evading me.

    Thus far, this is what i did,

    I added a boolean variable to my class obj that fills up an array which intern populates the tableview.

    In cellForRowAtIndexPath I added a check. I'm using a customCell class and it already had a bool (checked)
    Code:
    cell.checked = Fruit.bool;
    
    Finally in didSelectRowAtIndexPath:

    Code:
    FruitClass *fruit = (FruitClass *)[AppDelegate.FruitArray objectAtIndex.row];
    fruit.bool = ! fruit.bool;
    
    Uptil now, everything works great. I thought i should stop. The only issue was that this required a scroll. The cell had to dissappear and appear again for the checked status to show. That was obvious since theres no action to change the status immediately.

    I then added this to didSelectRowAtIndexPath

    Code:
    CustomCell *targetCustomCell = (CustomCell *)[self. tableView cellForRowAtIndexPath:indexPath];
    [targetCustomCell checkAction:nil];
    
    The following is in CustomCell.m

    Code:
    UIImage *checkImage = (self.checked) ? [UIImage imageNamed:@"c1"] : [UIImage imageNamed:@"c2"];
    [checkButton setImage:checkImage forState:UIControlStateNormal];
    This does the job alright, but i'm back to the original problem, all reused cells are now checked and not the one i choose.

    Any solution here? I'm getting some ideas, but their kinda jumbling up the code. And for wat i've read, thats one thing to be avoided in obj C.

    thanks for your replies.
     

Share This Page