【问题标题】:UISwitch and viewWithTag is nil in numberOfRowsInSectionUISwitch 和 viewWithTag 在 numberOfRowsInSection 中为零
【发布时间】:2012-11-29 01:24:05
【问题描述】:

我正在尝试找出向 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 随时被释放。单元格被重复使用,但是节标题呢?

最后,我读过this post,虽然关于标签使用的建议是有道理的,但它似乎根本不喜欢标签方法。如果您不觉得使用标签很麻烦并且正在智能地使用它们,为什么不使用它们呢?为什么标签功能甚至可用?

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

干杯!

【问题讨论】:

    标签: ios null


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

    【讨论】:

    • 感谢您的回复。这实际上是我更喜欢来自 Microsoft 堆栈的一种方法(“将 UI 控件的属性绑定到相应的数据模型对象的值”)。鉴于 UISwitch 很可能不再可供参考,我可能会采用这种方法,但在此之前,我有一个哲学编程问题要问您:您如何看待here 的观点?
    • (续 .... )它建议在 UISwitch(在我们的例子中)上跟踪数据模型对象索引,而不是使用标签。您如何看待这种方法?矫枉过正?不在乎? “就这么办好?” :-) 我也在尝试完全理解 .tag 属性的核心及其用途。
    • 我想我在这个“它是矫枉过正”阵营。我不确定他的一些方法在处理在滚动时不断创建和释放的表视图中的视图时如何工作。我希望有一些类似于 indexPathForSelectedRow 的方法,比如 sectionForSelectedView,用于页眉和页脚视图,但除非我遗漏了什么,否则没有,所以我们遇到了一些更混乱的事情。
    • 我想我和你在一起。无论如何,我现在假设(参见下面的第二个答案),viewForHeaderInSection 中的表使用的 UIView 被释放(因此它也是子视图),因此别无选择,只能使用这种数据绑定方法。我会考虑回答的问题。感谢您的帮助!
    【解决方案2】:

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

    【讨论】:

    猜你喜欢
    • 2021-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多