简体   繁体   English

包含UISwitch更新的UITableView单元格不一致

[英]UITableView cells containing a UISwitch update inconsistently

I have a UITableView where each cell contains a UISwitch . 我有一个UITableView ,其中每个单元格包含一个UISwitch The switch is supposed to toggle the content named in the cell as active or not, and it works perfectly when the user toggles the switch. 该开关应该切换单元格中命名为活动的内容或不激活的内容,并且当用户切换该开关时,它可以完美地工作。

However, then I got the bright idea of also doing the toggling when the entire table row is selected. 但是,然后我想到了一个聪明的主意,即在选择整个表行时也进行切换。 Simple in theory right? 理论上简单吧? And it was, in terms of updating the data model. 而且,就更新数据模型而言。

However, for whatever reason that has had me stumped for the last 2 hours, only the switch in cell #3 updates to reflect the state change when I reload the table data. 但是,无论出于什么原因使我在过去2个小时内都感到困惑,重新加载表数据时,只有单元格3中的开关才会更新以反映状态更改。 No matter how many rows I have in the table, it is always only the third cell that works. 无论表中有多少行,始终只有第三个单元起作用。 If there are less than 3 cells, then no cells work. 如果少于3个单元,则没有单元起作用。

I've dumped a load of NSLog calls in there and they reflect the proper data states as I'd expect at all times, but they just aren't being reflected in the switch beyond the first load. 我在其中转储了一个NSLog调用负载,它们始终反映出我所希望的正确数据状态,但是它们并没有反映在第一次加载之后的切换中。 If I leave the view controller then come back, the switches are all properly set... 如果我离开视图控制器然后回来,则所有开关都已正确设置...

I just know this is going to be some stupid thing I've done somewhere, because it works in exactly one instance, but for the life of me I cannot see it :( 我只是知道这将是我在某处所做的一些愚蠢的事情,因为它仅在一个实例中起作用,但是对于我一生,我看不到它:(

Can you? 你是否可以?

-(void)viewDidLoad
{
    [super viewDidLoad];

    [_tableAddPeople setDelegate:self];
    [_tableAddPeople setDataSource:self];
}

-(void)viewWillAppear:(BOOL)animated
{
    [_tableAddPeople reloadData];
}

-(void)listSwitchChanged:(id)sender
{
    UISwitch * switchControl = sender;

    Person * person = [SAVE_DATA getPersonWithIndex:(int)switchControl.tag];

    if (switchControl.on)
    {
        [SAVE_DATA activePeopleAdd:person.tag];
    }
    else
    {
        [SAVE_DATA activePeopleRemove:person.tag];
    }
}


#pragma mark UITableViewDataSource Delegate

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [SAVE_DATA peopleCount];
}

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

    UILabel * nameLabel = (UILabel *)[cell.contentView viewWithTag:1];
    UISwitch * activeSwitch = (UISwitch *)[cell.contentView viewWithTag:2];

    Person * person = [SAVE_DATA getPersonWithIndex:(int)[indexPath row]];

    [nameLabel setText:[person name]];

    BOOL active = [SAVE_DATA activePeopleContains:person.tag];
    NSLog(@"Row for %@ is %@", person.name, (active? @"ON" : @"OFF"));
    [activeSwitch setOn:active animated:YES];
    [activeSwitch addTarget:self action:@selector(listSwitchChanged:) forControlEvents:UIControlEventValueChanged];
    activeSwitch.tag = (int)[indexPath row];

    return cell;
}

-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    return NO;
}

-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    return NO;
}


#pragma mark UITableView Delegate

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    Person * person = [SAVE_DATA getPersonWithIndex:(int)[indexPath row]];

    if ([SAVE_DATA activePeopleContains:person.tag])
    {
        NSLog(@"Removing %@", person.name);
        [SAVE_DATA activePeopleRemove:person.tag];
    }
    else
    {
        NSLog(@"Adding %@", person.name);
        [SAVE_DATA activePeopleAdd:person.tag];
    }

    [_tableAddPeople reloadData];
    //dispatch_async(dispatch_get_main_queue(), ^{[tableView reloadData];});
}

Your problem is that you are using the switch tag property for two things: 您的问题是您将switch tag属性用于两件事:

  1. To identify it in the view hierarchy (You have assigned value '2') 在视图层次结构中标识它(您已将值分配为“ 2”)
  2. To determine in which row the switch was changed 确定更改开关在哪一行

Because you are assigning indexPath.row to tag , in the case where the index path isn't 2 (ie the third row), [cell.contentView viewWithTag:2]; 因为您是将indexPath.row分配给tag ,所以在索引路径不是2(即第三行)的情况下, [cell.contentView viewWithTag:2]; will return nil when the cell is re-used. 重用该单元格时将返回nil

You also have an issue with cell re-use because you are adding the switch action handler multiple times. 您还存在单元重用的问题,因为您要多次添加switch操作处理程序。

I would suggest that you adopt the following approach: 我建议您采用以下方法:

  1. Expose your cell's sub views using properties and cast the result of dequeueReusableCell to your UITableViewCell subclass so that you can access them without needing to use the tag 使用属性公开单元的子视图,并将dequeueReusableCell的结果dequeueReusableCellUITableViewCell子类,以便无需使用标签即可访问它们

  2. Establish your UITableViewCell subclass as the switch action handler. 建立UITableViewCell子类作为切换操作处理程序。

  3. Implement a delegation pattern between the cell and your view controller, so that the cell can notify the view controller that the switch changed. 在单元格和视图控制器之间实现委派模式,以便单元格可以通知视图控制器该开关已更改。 The cell can pass self to the delegate method. 单元格可以将self传递给委托方法。 In the delegate method you can use indexPathForCell to determine the affected row. 在委托方法中,可以使用indexPathForCell确定受影响的行。

  4. Don't reload the whole tableview just because one row has changed. 不要仅因为一行已更改而重新加载整个tableview。 Only reload the affected row. 仅重新加载受影响的行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM