UISwitch和viewWithTag在numberOfRowsInSection中为零

时间:2012-11-29 01:24:06

标签: ios null

我正在尝试找出向UITableView标头添加和管理UISwitch的最佳方法,到目前为止,我已经阅读了几个关于如何在表格中最好地使用UISwitch的链接:here,{ {3}}和here。这些链接演示了在单元格中使用UISwitch,而我在自定义UIView中使用它们作为标题。也就是说,我更愿意使用标签来管理这些对象,但我无法弄清楚为什么viewWithTag方法无效。

附注:我能够在运行时将UISwitches放入NSMutableArray并以这种方式管理它们,但我宁愿不那么冗长,管理数组上的边界违规/索引或检查nil列表等等...我还不清楚如何使用IBOutlets做到这一点。这就是我正在尝试标记方法的原因。

我的目标是使用开关来折叠/展开每个部分中的行,这就是为什么我想要将UISwitch作为子视图添加到我在viewForHeaderInSection中返回的UIView的原因。然后在我需要执行一些逻辑来折叠单元格时重新引用它们。另外,在运行时我可能有一个或多个部分,因此对标签号进行硬编码是不切实际的。这是该方法的代码:

假设:

#define kSwitchTagRange 2000
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{    
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, 44)];

    // Add UISwitches and ensure that UISwitch doesn't already exist for this section...
    UISwitch *switchView = (UISwitch*)[self.tableView viewWithTag:(kLayerSwitchTagRange + section)];

    if (switchView == nil) {

        // Create switch
        switchView = [[UISwitch alloc] init];
        double xOffset = self.tableView.bounds.size.width - switchView.frame.size.width;
        switchView.frame = CGRectMake(xOffset,
                                      7,
                                      switchView.frame.size.width,
                                      switchView.frame.size.height);

        [switchView addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged];

        // Should we tag the switch view?
        switchView.tag = kSwitchTagRange + section;
    }

    // Add switch as subview
    [view addSubview:switchView];    

    return view;
}

在switchChanged中:我只是重新加载表的数据:

- (void)switchChanged:(id)sender {

     [self.tableView reloadData];
}

最后,当重新创建表的数据时,我尝试检索UISwitch并确定它处于开/关状态,然后如果OFF则返回0表示该行中的行数,如果为ON则返回其他一些数字:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    NSInteger rowCount = 0;

    UISwitch *switchView = (UISwitch*)[self.view viewWithTag:(kSwitchTagRange + section)];

    if (switchView != nil && switchView.isOn == NO)
    {
        rowCount = 0;

    }
    else
    {
        // Row count is something other than 0
        rowCount = 3;
    }

    return rowCount;
}

不幸的是,这个switchView始终是零。

我有一些猜测,但不能确定为什么会发生这种情况。

  • 猜测1:当重新加载表的数据时,添加了我想要的开关的UIView被释放。它在内存中不存在,因此无法通过标记进行搜索。
  • 猜猜2:我正在将错误的UISwitch添加到视图对象中(在很多示例中,我已经看到对象添加到UITableViewCell的contentView中,但是因为我在viewForHeaderInSection:方法中发回了一个UIView,我是不确定这些示例是否适用于此.addSubview应该将任何对象添加到树中。

有谁能告诉我为什么上面的viewWithTag方法会返回nil?我倾向于猜测#1但是没有找到任何文档告诉我UIView随时被解除分配。这些单元格被重用了,但是节标题呢?

最后,我已阅读here,虽然有关标记使用的建议有意义但似乎完全不喜欢标记方法。为什么不使用标签,如果你没有发现使用它们是凌乱的,他们被智能地使用?为什么标签功能可用?

真的,我只是想知道为什么viewWithTag方法在我的情况下返回nil。

干杯!

2 个答案:

答案 0 :(得分:1)

我不确定为什么你的viewWithTag总是返回nil,我认为这可能与视图在某些时候被释放的事实有关。

但是,标签仍然可以按您的需要进行操作,但您需要在模型对象中使用属性或键来跟踪开关的值。您可以使用标记在其操作方法中告知交换机所在的部分,并相应地更新模型。这就是我做的。我的模型是一个字典数组,其中键“rowData”的值是一个包含该部分所有数据的数组,键“switchValue”的值是NSNumber,0或1,表示开关的状态。所以,我的数据看起来像这样:

2012-11-28 22:50:03.104 TableWithSwitchesInHeaderView[3592:c07] (
        {
        rowData =         (
            One,
            Two,
            Three,
            Four
        );
        switchValue = 1;
    },
        {
        rowData =         (
            Red,
            Orange,
            Yellow,
            Green,
            Blue,
            Violet
        );
        switchValue = 1;
    },
)

这就是我在相关表视图中的委托和数据源方法:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.theData.count;
}

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, 44)];
    UISwitch *switchView = [[UISwitch alloc] init];
    double xOffset = self.tableView.bounds.size.width - switchView.frame.size.width;
    switchView.frame = CGRectMake(xOffset,7,switchView.frame.size.width,switchView.frame.size.height);
    [switchView addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
    switchView.tag = kSwitchTagRange + section;
    switchView.on = [[[self.theData objectAtIndex:switchView.tag - 101] valueForKey:@"switchValue"] boolValue];
    [view addSubview:switchView];
    return view;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 50;
}

- (void)switchChanged:(UISwitch *)sender {
    NSMutableDictionary *dict = [self.theData objectAtIndex:sender.tag - 101];
    [dict setValue:[NSNumber numberWithBool:sender.isOn] forKey:@"switchValue"];
    [self.tableView reloadData];
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    BOOL switchValue = [[self.theData[section] valueForKey:@"switchValue"] boolValue];
    if (switchValue) {
        return [[self.theData[section] valueForKey:@"rowData"] count];
    }else{
        return 0;
    }

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    cell.textLabel.text = [[self.theData[indexPath.section]valueForKey:@"rowData"]objectAtIndex:indexPath.row] ;
    return cell;
}

我的kSwitchTagRange #defined为101.此代码用于在交换机关闭的任何部分中折叠行(实际上除去所有行)。

答案 1 :(得分:0)

从帖子here看来,viewForHeaderInSection返回的UIView已被释放,必须重新创建(以及所有子视图,包括我的UISwitch),而不是重新使用。我相信Apple假设您的节标题比单元格少得多,因此没有提供用于检索节标题的可重用性机制。 @rdelmar提出的数据绑定方法似乎对这种情况有意义。