views:

373

answers:

2

Hi all,

I'm learning about the iPhone SDK and have an interesting exception with UITableViewCell subview management when a finger is pressed on some rows.

The table is used to assign sounds to hand gestures -- swiping the phone in one of 3 directions triggers the sound to play. Selecting a row displays an action sheet with 4 options for sound assignment: left, down, right, and cancel. Sounds can be mapped to one, two, or three directions so any cell can have one of seven states: left, down, right, left and down, left and right, down and right, or left down and right. If a row is mapped to any of these seven states, a corresponding arrow or arrows are displayed within the bounds of the row as a subview. Arrows come and go as they should in a given screen and when scrolling around.

However, after scrolling to a new batch of rows, only when I press my finger down on some (but not all) rows, does an arrow magically appear in the selected state background. When I lift my finger off the row, and the action sheet appears, the arrow disappears. After pressing any of the four buttons, I can't replicate this anymore. But it's really disorienting and confusing to see this arrow flash on screen because the selected row isn't assigned to anything.

What haven't I thought to look into here? All my table code is pasted below and this is a screencast of the problem: http://www.screencast.com/users/JonathanGCohen/folders/Jing/media/d483fe31-05b5-4c24-ab4d-70de4ff3a0bf

Am I managing my subviews wrong or is there a selected state property I'm missing? Something else? Should I have included any more information in this post to make things clearer? Thank you!!

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

    NSUInteger section = [indexPath section];
    NSUInteger row = [indexPath row];
    NSString *key = [categories objectAtIndex:section];
    NSArray *nameSection  = [categoriesSounds objectForKey:key];
    static NSString *SectionsTableIdentifier = @"SectionsTableIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
                             SectionsTableIdentifier];
    NSArray *sound = [categoriesSounds objectForKey:key];
    NSString *soundName = [[sound objectAtIndex: row] objectAtIndex: 0];
    NSString *soundOfType = [[sound objectAtIndex: row] objectAtIndex: 1];

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

    cell.textLabel.text = [[nameSection objectAtIndex:row] objectAtIndex: 0];

    NSUInteger soundSection = [[[sound objectAtIndex: row] objectAtIndex: 2] integerValue];
    NSUInteger soundRow = [[[sound objectAtIndex: row] objectAtIndex: 3] integerValue];

        NSUInteger leftRow = [leftOldIndexPath row];
        NSUInteger leftSection = [leftOldIndexPath section];
        if (soundRow == leftRow && soundSection == leftSection && leftOldIndexPath !=nil){
            [selectedSoundLeftAndDown removeFromSuperview];
            [selectedSoundLeftAndRight removeFromSuperview];
            [cell.contentView addSubview: selectedSoundLeft];
            selectedSoundLeft.frame = CGRectMake(200,8,30,30);
        }
        else {
            [cell.contentView sendSubviewToBack: selectedSoundLeft];
        }

        NSUInteger downRow = [downOldIndexPath row];
        NSUInteger downSection = [downOldIndexPath section];
        if (soundRow == downRow && soundSection == downSection && downOldIndexPath !=nil){
            [selectedSoundLeftAndDown removeFromSuperview];
            [selectedSoundDownAndRight removeFromSuperview];
            [cell.contentView addSubview: selectedSoundDown];
            selectedSoundDown.frame = CGRectMake(200,8,30,30);
        }
        else {
            [cell.contentView sendSubviewToBack: selectedSoundDown];
        }

        NSUInteger rightRow = [rightOldIndexPath row];
        NSUInteger rightSection = [rightOldIndexPath section];
        if (soundRow == rightRow && soundSection == rightSection && rightOldIndexPath !=nil){
            [selectedSoundDownAndRight removeFromSuperview];
            [selectedSoundLeftAndRight removeFromSuperview];
            [cell.contentView addSubview: selectedSoundRight];
            selectedSoundRight.frame = CGRectMake(200,8,30,30);
        }
        else {
            [cell.contentView sendSubviewToBack: selectedSoundRight];
        }

        // combos
        if (soundRow == leftRow && soundRow == downRow &&
            soundSection == leftSection && soundSection == downSection){
            [selectedSoundLeft removeFromSuperview];
            [selectedSoundDown removeFromSuperview];
            [selectedSoundLeftAndDownAndRight removeFromSuperview];
            [cell.contentView addSubview: selectedSoundLeftAndDown];
            selectedSoundLeftAndDown.frame = CGRectMake(200,8,30,30);
        }
        else {
            [cell.contentView sendSubviewToBack: selectedSoundLeftAndDown];
        }
        if (soundRow == leftRow && soundRow == rightRow &&
            soundSection == leftSection && soundSection == rightSection){
            [selectedSoundLeft removeFromSuperview];
            [selectedSoundRight removeFromSuperview];
            [selectedSoundLeftAndDownAndRight removeFromSuperview];
            [cell.contentView addSubview: selectedSoundLeftAndRight];
            selectedSoundLeftAndRight.frame = CGRectMake(200,8,30,30);
        }
        else {
            [cell.contentView sendSubviewToBack: selectedSoundLeftAndRight];
        }
        if (soundRow == downRow && soundRow == rightRow &&
            soundSection == downSection && soundSection == rightSection){
            [selectedSoundDown removeFromSuperview];
            [selectedSoundRight removeFromSuperview];
            [selectedSoundLeftAndDownAndRight removeFromSuperview];
            [cell.contentView addSubview: selectedSoundDownAndRight];
            selectedSoundDownAndRight.frame = CGRectMake(200,8,30,30);
        }
        else {
            [cell.contentView sendSubviewToBack: selectedSoundDownAndRight];
        }
        if (soundRow == leftRow && soundRow == downRow && soundRow == rightRow &&
            soundSection == leftSection && soundSection == downSection && soundSection == rightSection){
            [selectedSoundLeftAndDown removeFromSuperview];
            [selectedSoundLeftAndRight removeFromSuperview];
            [selectedSoundDownAndRight removeFromSuperview];
            [selectedSoundLeft removeFromSuperview];
            [selectedSoundDown removeFromSuperview];
            [selectedSoundRight removeFromSuperview];
            [cell.contentView addSubview: selectedSoundLeftAndDownAndRight];
            selectedSoundLeftAndDownAndRight.frame = CGRectMake(200,8,30,30);
        }
        else {
            [cell.contentView sendSubviewToBack: selectedSoundLeftAndDownAndRight];
        }

    [indexPath retain];
    return cell;    
}

Just wanted to put an update on this post. I spent 12 hours yesterday trying to fix this and it's just beyond my current skills. I set the cell selection style to none, which sucks but it's time to throw in the towel for now.

I completely ditched that code in favor of the following approach, which checked all currently visible index paths for the presence of an index path assigned to a sound and then removed subviews as appropriate:

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


/*  NSArray *allVisibleCells = [table visibleCells];

    leftIsVisible = FALSE;
    downIsVisible = FALSE;
    rightIsVisible = FALSE;

    NSLog(@"\n\n\n", (leftIsVisible ? @"TRUE" : @"FALSE"));

    NSLog(@"Start: %@\n", (leftIsVisible ? @"TRUE" : @"FALSE"));


    for (UITableViewCell *visibleCell in allVisibleCells){

        NSIndexPath *visibleCellIndexPath = [table indexPathForCell: visibleCell];
        NSLog(@"visible cell index path %@\n", visibleCellIndexPath);

        if ([visibleCellIndexPath compare: leftOldIndexPath] == NSOrderedSame) {
            leftIsVisible = TRUE;
            NSLog(@"Compare successful: %@\n", (leftIsVisible ? @"TRUE" : @"FALSE"));
        }
        if ([visibleCellIndexPath compare: downOldIndexPath] == NSOrderedSame) {
            downIsVisible = TRUE;
        }
        if ([visibleCellIndexPath compare: rightOldIndexPath] == NSOrderedSame){
            rightIsVisible = TRUE;
        }
    }

    NSLog(@"After the first fast enumeration: %@\n", (leftIsVisible ? @"TRUE" : @"FALSE"));
    NSLog(@"After check for left is still visible %@\n", (leftIsVisible ? @"TRUE" : @"FALSE"));

    if(leftIsVisible == FALSE){
        [selectedSoundLeft removeFromSuperview];
        [selectedSoundLeftAndDown removeFromSuperview];
        [selectedSoundLeftAndRight removeFromSuperview];
        [selectedSoundLeftAndDownAndRight removeFromSuperview];
    } 
    if(downIsVisible == FALSE){
        [selectedSoundDownAndRight removeFromSuperview];
        [selectedSoundDown removeFromSuperview];
    }
    if (rightIsVisible == FALSE){
        [selectedSoundRight removeFromSuperview];
    }   */
+1  A: 

I don't have time to wade through 325 lines of code but the most likely cause of your problem is the reuse of tableview cells without having cleared their subviews first.

You're seeing the arrows appear in unassigned cells because the actual cell objects are being reused from the very first few rows displayed. Since you have a custom subview, you have to manage that subview manually every time the cell changes info.

Each time you reuse a cell your first step should be to wipe it clean to it's initial start state. Only then should you load new data into the cell.

TechZen
A: 

The correct way (well the way the Apple Docs say) is to use tags on all the subviews. If the cell is being created (not dequeued/reused) then you create the cell as normal but set a unique tag (integer, though you can use define to make it easier to read). If the cell is being dequeued/reused then you get the cell using the tags. Look in the Apple Docs online for customizing UItableViews and theres a section about customizing tableviewcells.

Jonathan