包含 UISwitch 的 UITableView 单元格更新不一致
UITableView cells containing a UISwitch update inconsistently
我有一个 UITableView
,其中每个单元格包含一个 UISwitch
。该开关应该将单元格中命名的内容切换为活动或不活动,并且当用户切换开关时它可以完美地工作。
然而,当整个 table 行被选中时,我想到了也进行切换的好主意。理论上很简单吧?在更新数据模型方面确实如此。
但是,无论出于何种原因让我在过去的 2 小时内感到困惑,当我重新加载 table 数据时,只有单元格 #3 中的开关更新以反映状态变化。无论我在 table 中有多少行,它总是只有第三个单元格有效。如果少于 3 个单元格,则没有单元格有效。
我已经将大量 NSLog
调用转储到那里,它们反映了我一直期望的正确数据状态,但它们只是在第一个之后没有反映在开关中加载。如果我离开视图控制器然后回来,开关都已正确设置...
我只知道这将是我在某个地方做过的一些愚蠢的事情,因为它只在一个实例中起作用,但对于我的生活我看不到它:(
可以吗?
-(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];});
}
你的问题是你正在使用开关 tag
属性 来做两件事:
- 在视图层次结构中识别它(您已分配值“2”)
- 确定在哪一行更改了开关
因为您将 indexPath.row
分配给 tag
,在索引路径不是 2(即第三行)的情况下,[cell.contentView viewWithTag:2];
将 return nil
当单元格被重新使用时。
您还遇到了单元格重用的问题,因为您要多次添加切换操作处理程序。
我建议您采用以下方法:
使用属性公开您的单元格的子视图,并将 dequeueReusableCell
的结果转换为您的 UITableViewCell
子类,这样您就可以访问它们而无需使用标签
将您的 UITableViewCell
子类设置为开关操作处理程序。
在单元格和您的视图控制器之间实施委托模式,以便单元格可以通知视图控制器开关已更改。单元格可以将 self
传递给委托方法。在委托方法中,您可以使用 indexPathForCell
来确定受影响的行。
不要仅仅因为某一行发生变化就重新加载整个表视图。仅重新加载受影响的行。
我有一个 UITableView
,其中每个单元格包含一个 UISwitch
。该开关应该将单元格中命名的内容切换为活动或不活动,并且当用户切换开关时它可以完美地工作。
然而,当整个 table 行被选中时,我想到了也进行切换的好主意。理论上很简单吧?在更新数据模型方面确实如此。
但是,无论出于何种原因让我在过去的 2 小时内感到困惑,当我重新加载 table 数据时,只有单元格 #3 中的开关更新以反映状态变化。无论我在 table 中有多少行,它总是只有第三个单元格有效。如果少于 3 个单元格,则没有单元格有效。
我已经将大量 NSLog
调用转储到那里,它们反映了我一直期望的正确数据状态,但它们只是在第一个之后没有反映在开关中加载。如果我离开视图控制器然后回来,开关都已正确设置...
我只知道这将是我在某个地方做过的一些愚蠢的事情,因为它只在一个实例中起作用,但对于我的生活我看不到它:(
可以吗?
-(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];});
}
你的问题是你正在使用开关 tag
属性 来做两件事:
- 在视图层次结构中识别它(您已分配值“2”)
- 确定在哪一行更改了开关
因为您将 indexPath.row
分配给 tag
,在索引路径不是 2(即第三行)的情况下,[cell.contentView viewWithTag:2];
将 return nil
当单元格被重新使用时。
您还遇到了单元格重用的问题,因为您要多次添加切换操作处理程序。
我建议您采用以下方法:
使用属性公开您的单元格的子视图,并将
dequeueReusableCell
的结果转换为您的UITableViewCell
子类,这样您就可以访问它们而无需使用标签将您的
UITableViewCell
子类设置为开关操作处理程序。在单元格和您的视图控制器之间实施委托模式,以便单元格可以通知视图控制器开关已更改。单元格可以将
self
传递给委托方法。在委托方法中,您可以使用indexPathForCell
来确定受影响的行。不要仅仅因为某一行发生变化就重新加载整个表视图。仅重新加载受影响的行。