【问题标题】:How do I retrieve UITableView row number of a UISwitch?如何检索 UISwitch 的 UITableView 行号?
【发布时间】:2013-10-16 05:47:29
【问题描述】:

我已经尝试了这里发布的几种方法,但我无法让我的表充满开关来返回已更改开关的单元格的索引值。我正在以编程方式创建包含表的视图(没有 xib)。

TableSandboxAppDelegate.m 我在didFinishLaunchingWithOptions: 中实例化视图控制器:

...
TableSandboxViewController *sandboxViewController = [[TableSandboxViewController alloc]
    init];
[[self window] setRootViewController:sandboxViewController];
...

TableViewController.h 文件内容如下:

@interface TableSandboxViewController : UITableViewController
{
   NSMutableArray *_questionOrder;
   NSMutableArray *switchStates;
}
@end

TableViewController.m cellForRowAtIndexPath: 读取:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MainCell"];

UISwitch *theSwitch = nil;

if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
        reuseIdentifier:@"MainCell"];

    theSwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
    theSwitch.tag = 100;
    [theSwitch addTarget:self action:@selector(switchChanged:)   
        forControlEvents:UIControlEventValueChanged];
    [cell.contentView addSubview:theSwitch];

} else {
    theSwitch = [cell.contentView viewWithTag:100];
}

if ([[switchStates objectAtIndex:indexPath.row] isEqualToString:@"ON"]) {
    theSwitch.on = YES;
} else {
    theSwitch.on = NO;
}

return cell;

TableViewController.m -(IBAction)switchChanged:(UISwitch *)sender 读取:

UITableViewCell *theParentCell = [[sender superview] superview];
NSIndexPath *indexPathOfSwitch = [self.tableView indexPathForCell:theParentCell];

NSLog(@"Switch changed at index: %d", indexPathOfSwitch.row);

我的日志结果始终是“Switch changed at index: 0”。我觉得问题出在 CGPoint 行中,我尝试了“sender”([sender superview]、[[sender superview]superview] 等)的替换组合。我不觉得那条线指向显示表格的视图。

我做错了什么?

在美国东部夏令时间 10 月 9 日 9:15 添加注释: 我的目标是能够处理表中大约 100 个是/否问题,因此重用是关键。我想滚动并让表格了解每个开关的状态,并在离开视图时能够检索它们。

【问题讨论】:

    标签: iphone ios objective-c uitableview uiswitch


    【解决方案1】:

    标签是一个不错的解决方案,但有点笨拙,因为单元格 - 以及它们的子视图 - 不断被重用,改变它们的行 - 从而改变它们需要的标签。

    相反,我通常会保留其中一个:

    - (NSIndexPath *)indexPathWithSubview:(UIView *)subview {
    
        while (![subview isKindOfClass:[UITableViewCell self]] && subview) {
            subview = subview.superview;
        }
        return [self.tableView indexPathForCell:(UITableViewCell *)subview];
    }
    

    然后当我收到 IBAction 时:

    - (IBAction)someSubviewAction:(id)sender {
    
        NSIndexPath *indexPath = [self indexPathWithSubview:(UIView *)sender];
        // carry on from here
    }
    

    【讨论】:

    • 我将您的 indexPathWithSubview 方法放入我的 TableSandboxViewController.m 文件中,并将 *indexPath 声明放入我的 switchChanged: 方法中,称为 indexPath 到我的 NSLog 并且仍然得到 (null) 作为结果.同样的,当我上下滚动时,会出现switch在视图中随机切换YES/NO的复用问题。
    • 这很聪明!.. 但是应该 while (![subview isKindOfClass:[UITableViewCell self]] 不是 while (![subview isKindOfClass:[UITableViewCell class]] ?
    • 考虑一下......同样的事情!我的错,继续:)
    • @danh 你能解释一下 while 条件检查的内容吗?抱歉,我是这种语言的新手。
    • @Shantanu - while 遍历层次结构以查找表视图单元格父级或祖先。它在找到一个或到达根时停止。
    【解决方案2】:

    您可以将切换视图标记设置为行索引。而不是theSwitch.tag = 100;

    -(UITableViewCell*)tableView:table cellForRowAtIndexPath:indexPth
    {
        UISwitch *theSwitch = nil;
        if (cell == nil) {
            ...
            // as per your example
            [cell.contentView addSubview:theSwitch];
        } else {
            theSwitch = subviewWithClass(cell.contentView, [UISwitch class]);
        }
    
        theSwitch.tag = indexPath.row;
        ...
    }
    

    添加这个辅助函数来替换viewWithTag:调用

    UIView *subviewWithClass(UIView *contentview, Class klass)
    {
        for (UIView *view in contentview.subviews)
            if ([view isKindOfClass:klass])
                return view;
        return nil;
    }
    

    然后在你的 switchChanged 函数中检索标签,即现在的行索引

    -(IBAction)switchChanged:(UISwitch *)sender {
        NSLog(@"Selected Switch - %d", sender.tag);
        ...
     }
    

    【讨论】:

    • 当我用你的替换我的theSwitch.tag 行并将你的 NSLog 语句放在我的switchChanged: 方法中时,我确实得到了所选开关行的正确日志输出,但是当我向上滚动/向下,开关在YES/NO之间随机变化。 (我的示例中显示了 20 行)。因此,重用“功能”以某种方式通过重绘一些开关及其状态来绘制新单元格。
    • @dmccall 您需要在if(cell==nil) 语句之外设置标签属性。我更新了我的答案。
    • @dmccall YES/NO 问题与您的原始问题无关。使用-(void)setOn:(BOOL)on animated:(BOOL)animated 函数来设置开关的状态,而不是on 属性。
    • 我认为这是对标签的滥用。 doing-it-wrong.mikeweller.com/2012/08/…
    • @vikingosegundo 一个色彩斑斓但固执己见的博客条目 :) Apple 文档:“标签 - 一个整数,可用于识别应用程序中的视图对象。”行索引是正式的标识符,因此在此处按照规范使用。支持或反对我建议的使用没有太多技术原因。归结为个人喜好。此处列出的其他建议有其自身的缺点。最纯粹的是Jonah.at。我个人很喜欢dahn 的那个。代码越少越好。
    【解决方案3】:

    如果您使用基于块的东西(例如https://github.com/brightsoftdev/iOS-Block-Based-Bindings/blob/master/UISwitch%2BBindings.m),则无需担心获取行,因为您可以引用传递给 tableView:cellForRowAtIndexPath: 的 indexPath: 在您的块中。

    【讨论】:

      【解决方案4】:

      与@danh 类似,我使用已多次使用的扩展提出了此解决方案。

      @interface UIView (Find)
      - (id)findSuperviewOfClass:(Class)class;
      - (NSIndexPath *)findIndexPath;
      @end
      
      @implementation UIView (Find)
      - (id)findSuperviewOfClass:(Class)class
      {
          return [self isKindOfClass:class] ? self : [self.superview findSuperviewOfClass:class];
      }
      
      - (NSIndexPath *)findIndexPath
      {
          UITableView *tableView = [self findSuperviewOfClass:[UITableView class]];
          return [tableView indexPathForCell:[self findSuperviewOfClass:[UITableViewCell class]]];
      }
      @end
      

      【讨论】:

        【解决方案5】:

        对于 iOS6+,您可以维护一个 NSMutableArray queuedSwitches

        -tableView:cellForrowAtIndexPath: 中,如果不为空,您将使用一个开关并将其放置在自定义单元格上并将其分配给一个属性。如果为空,则创建一个新的。

        -tableView:didEndDisplayingCell:forRowAtIndexPath: 中,您可以将其添加到quededSwitches 并将其从单元格中删除。

        这将为可见单元分配足够的开关并重复使用它们。

        所有开关都连接到一个动作。

        -(void)switchAction:(UISwitch *)switch 
        {
            NSIndexPath *indexPath = [self.tableView indexPathForCell:[switch superView]];
        
            //…
        }
        

        【讨论】:

          【解决方案6】:

          您可以创建 UISwitch 的子类并添加 indexPath 属性,然后只需在 cellForRowAtIndexPath 中设置 indexPath:

          -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
          {
              SwitchCell *returnCell = [tableView dequeueReusableCellWithIdentifier:@"SwitchCell" forIndexPath:indexPath];
              returnCell.switch.indexPath = indexPath;
          
              return returnCell;
          }
          

          【讨论】:

            猜你喜欢
            • 2014-02-05
            • 2016-10-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-10-18
            • 1970-01-01
            • 1970-01-01
            • 2011-04-15
            相关资源
            最近更新 更多