3

Hello there i found an issue on my app that is maybe not critical but strange.

I have in my viewController a switch, let's call it theSwitch, when I populate the cells, on indexPath (0,0) i put that switch as the accessory view.

The title of the cell changes depending of the state of the switch

//Code simplified for simplicity 
if(indexPath.section == 0 && indexPath.row == 0) {
     cell.textLabel.text = self.filtersActivatedSwitch.on ? NSLocalizedString(@"Filters activated", nil) : NSLocalizedString(@"Filters deactivated", nil);
     cell.accessoryView = self.filtersActivatedSwitch;
}

When the switch is pressed this method is called

-(void)switchActivated {
     [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:VisibilityFilterTableViewSectionMasterFilter]];
}

This will create a deadlock because on the method cellForRowAtIndexPath when i try to dequeue the cell it will create a new one instead (this is tested, i thought it made more sense that it used the same cell that i wanted to reload) and in the configuration of the cell the switch will be added as accessoryView...

This is where it gets tricky...

BadPirate's answer explain when layoutSubview is called on a tableviewcell

When is layoutSubviews called?

-addSubview causes layoutSubviews to be called on the view being added, the view it’s being added to (target view), and all the subviews of the target

I think the same should happen with removeFromSupperview (supposition), so the new cell has the layoutSubview called.. it checks the accessoryView, see that it is not nil so it add it ass subview, on the other hand in the old cell (that did not get dequeued for any reason so i couldn't clear the accessoryView) layoutSubviews gets called because of the removeFromSuperview (again supposition) it checks that the accessoryView is not nil, it adds the switch as subview... and repeat eternally.

My question is... why won't the table view use the already dequeued cell? the fact that two tableviewcells have the same accessoryView clearly will crash my app.

Or i'm ussing the accessory Views wrong?

Cheers!

EDIT: Here is the code on prepareForReuse

- (void)prepareForReuse {
  [super prepareForReuse];

  for (UIView *view in self.contentView.subviews) {
      [view removeFromSuperview];
  }

  for (UIView *view in self.imageView.subviews) {
      [view removeFromSuperview];
  }

  self.textLabel.text = nil;
  self.detailTextLabel.text = nil;
  self.imageView.image = nil;
  self.imageView.tag = 0;
  self.accessoryView = nil;
}
Community
  • 1
  • 1
Heavy_Bullets
  • 518
  • 1
  • 4
  • 13

1 Answers1

3

Cells are reused, so a cell that gets scrolled off screen can be reused in another location, with any subviews you added still there. When adding an accessory view at a specific indexPath, you need to have an "else" clause in there to tell the other cells to not have that view:. I don't know what else you're doing in cellForRowAtIndexPath:, but in my test method, I didn't have to use the prepareForReuse method at all. I'm not sure I understand why you get the deadlock when the switch method fires, but everything worked if you set the animation to UITableViewRowAnimationNone in the reloadRowsAtIndexPaths:withRowAnimation: method.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    if(indexPath.section == 0 && indexPath.row == 0) {
        cell.textLabel.text = self.filtersActivatedSwitch.on ? NSLocalizedString(@"Filters activated", nil) : NSLocalizedString(@"Filters deactivated", nil);
        cell.accessoryView = self.filtersActivatedSwitch;
    }else{
        cell.textLabel.text = self.theData[indexPath.row];
        cell.accessoryView = nil;
    }

    return cell;
}

-(void)switchActivated {

    [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
}
rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • cell.accessoryView = UITableViewCellAccessoryNone is wrong, that is an enum and accessoryView expects an object... you can pass nil (that's what i do before the if.. also this happens withouth scrolling, even if i only print 1 cell then updaterows the deadlock happens (in fact i overrides prepareForReuse and set all the accessoryViews to nil) – Heavy_Bullets Aug 31 '13 at 02:28
  • @Heavy_Bullets, yeah sorry, I was thinking accessoryType, not view. Is the code you posted in cellForRowAtIndexPath? If you're also implementing prepareForReuse, you should post that method as well as the whole cellForRow method. And what do you mean by deadLock -- what are you seeing? – rdelmar Aug 31 '13 at 04:45
  • thx, check edit, is not very fancy just sets the tableviewcell as naked as possible (on cellforrow it will be re populated) – Heavy_Bullets Aug 31 '13 at 19:58
  • @Heavy_Bullets, I've edited my post to show what worked for me -- the main thing was to have no animation when you reload the row. – rdelmar Aug 31 '13 at 21:08
  • this!... thanks a lot.. you saved me a bunch of headaches.. +1 and accepted answer – Heavy_Bullets Aug 31 '13 at 23:48