1

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

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

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

假设:

#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?我倾向于 Guess #1,但没有找到任何文档告诉我自定义标头 UIView 随时被释放。单元格被重复使用,但是节标题呢?

最后,我读过这篇文章,虽然关于标签使用的建议是有道理的,但它似乎根本不喜欢标签方法。如果您不觉得使用标签很麻烦,而且它们被巧妙地使用,为什么不使用它们呢?为什么标签功能甚至可用?

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

干杯!

4

2 回答 2

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。此代码用于折叠关闭开关的任何部分中的行(实际上删除了所有行)。

于 2012-11-29T07:03:13.440 回答
0

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

于 2012-11-29T18:06:06.090 回答