【问题标题】:How to know the UITableview row number如何知道 UITableview 的行号
【发布时间】:2012-03-05 16:18:47
【问题描述】:

我有一个UITableViewCellUISwitch 作为每个单元格的附件视图。当我在一个单元格中更改开关的值时,我如何知道开关在哪一行?我需要切换值更改事件中的行号。

【问题讨论】:

  • 您可以对开关进行子类化,添加属性“index”,并在创建单元格(和开关)时将此属性设置为单元格的当前索引。通过按下开关,您可以读取此属性,这就是您的单元格索引。
  • @TRD 每次从tableView:cellForRowAtIndexPath: 返回单元格时,您都必须更新索引属性,因为一个单元格可以重复用于不同的行。

标签: iphone ios uitableview uiswitch


【解决方案1】:

公认的解决方案是一种巧妙的技巧。

但是,如果我们可以利用 UIView 上已有的tag 属性,为什么还需要使用命中点呢?您会说 标签只能存储行或部分,因为它是一个 Int

嗯...不要忘记你的根源(CS101)。 单个 Int 可以存储两个大小小两倍的整数。 这是一个扩展:

extension Int {

    public init(indexPath: IndexPath) {
        var marshalledInt: UInt32 = 0xffffffff

        let rowPiece = UInt16(indexPath.row)
        let sectionPiece = UInt16(indexPath.section)
        marshalledInt = marshalledInt & (UInt32(rowPiece) << 16)
        marshalledInt = marshalledInt + UInt32(sectionPiece)

        self.init(bitPattern: UInt(marshalledInt))
    }

    var indexPathRepresentation: IndexPath {
        let section = self & 0x0000ffff

        let pattern: UInt32 = 0xffff0000
        let row = (UInt32(self) & pattern) >> 16
        return IndexPath(row: Int(row), section: Int(section))
    }
}

在您的tableView(_:, cellForRowAt:) 中,您可以:

cell.yourSwitch.tag = Int(indexPath: indexPath)

然后在动作处理程序中你可以:

func didToogle(sender: UISwitch){
    print(sender.tag.indexPathRepresentation)
}

但请注意它的限制:行和节不能大于 65535。 (UInt16.max)

我怀疑您的 tableView 的索引会变得那么高,但如果他们这样做,挑战自己并实施更有效的打包方案。假设我们有一个非常小的部分,我们不需要所有 16 位来表示一个部分。我们可以有这样的 int 布局:

{section area length}{all remaining}[4 BITS: section area length - 1]

这是我们的 4 个 LSB 表示节区域的长度 - 1,假设我们为一个节分配了至少 1 位。因此,如果我们的部分为 0,则该行最多可以占用 27 位([1][27][4]),这绝对足够了。

【讨论】:

    【解决方案2】:

    标签、子类或视图层次导航工作量太大!。在您的操作方法中执行此操作:

    CGPoint hitPoint = [sender convertPoint:CGPointZero toView:self.tableView]; 
    NSIndexPath *hitIndex = [self.tableView indexPathForRowAtPoint:hitPoint];
    

    适用于任何类型的视图、多节表,无论您可以扔给它什么 - 只要您的发件人的来源在单元格的框架内(感谢 rob!),通常情况就是这样。

    这里是 UITableView Swift 扩展:

    extension UITableView {
        func indexPath(for view: UIView) -> IndexPath? {
            let location = view.convert(CGPoint.zero, to: self)
            return self.indexPathForRow(at: location)
        }
    }
    

    【讨论】:

    • @GajendraKChauhan - 就像它在答案中所说的那样,在你的行动方法中。我的意思是当点击单元格中的按钮时调用的任何方法,其中按钮本身作为sender 传递
    • @Zac24 发送者是链接到操作方法的任何按钮或控件。此代码不会进入 cellForRowAtIndexPath,它会进入按钮或开关或任何添加到单元格的操作方法。
    • 我正在尝试添加 Swift 扩展,但我得到了,“声明仅在文件范围内有效。”有什么我想念的吗?
    • @deebs 您必须在文件的顶层添加扩展名,例如不在另一个班级或任何东西内
    • @DavidJirman Center 坐标空间错误,convert 方法已经考虑了视图层次结构。
    【解决方案3】:

    这篇文章中接受的答案非常好。我想向读者建议以下内容,源自这篇文章中的@robmayoff,也非常好:

    - (NSIndexPath *)indexPathForView:(UIView *)view inTableView:(UITableView *)tableView {
        while (view && ![view isKindOfClass:[UITableViewCell class]])
            view = view.superview;
        UITableViewCell *cell = (UITableViewCell *)view;
        return [tableView indexPathForCell:cell];
    }
    

    有些人断言,由于 while 循环,这种方法包含过多的计算工作。另一种方法是将视图的原点转换为表格视图坐标空间并调用indexPathForRowAtPoint:,从而隐藏更多工作。

    有些人声称这种方法相对于潜在的 SDK 更改是不安全的。事实上,Apple 已经更改过一次 tableview 单元格层次结构,在单元格中添加了一个contentView。这种方法在这种变化之前和之后都有效。只要可以通过 chain 超级视图(与 UIKit 中的任何东西一样基本)找到视图祖先,这就是很好的代码。

    【讨论】:

    • "Some" 对 while 循环有点过敏:D
    • @jrturton,这可能会让你打喷嚏更厉害:for (; view &amp;&amp; ![view isKindOfClass:[UITableViewCell class]]; view = view.superview)
    • 奔向山丘
    【解决方案4】:

    我不知道多个部分,但我可以给你一个部分...

    -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
    NSInteger index=indexPath.row;
    NSString *string=[[NSString alloc]initWithFormat:@"%ld",(long)index];
    }
    

    从中你可以得到行号,你可以把它保存到字符串中......

    【讨论】:

      【解决方案5】:

      使用 superView 的另一种变体。类似于 UIView 的类别。

      - (UITableViewCell *)superCell
      {
          if (!self.superview) {
              return nil;
          }
      
          if ([self.superview isKindOfClass:[UITableViewCell class]]) {
              return (UITableViewCell *)self.superview;
          }
      
          return [self.superview superCell];
      }
      

      【讨论】:

        【解决方案6】:

        一位同事提出以下建议,我把它做成了一个 UITableView 类别:

        +(UITableViewCell*)findParentCellForSubview:(UIView*)view
        {
            while (([view isKindOfClass:[UITableViewCell class]] == NO) && ([view superview] != nil))
                view = [view superview];
        
            if ([view superview] != nil)
                return (UITableViewCell*)view;
        
            return nil;
        }
        

        仍然很老套 - 但它可以工作。

        【讨论】:

          【解决方案7】:

          我更喜欢使用子视图,如果你知道你的布局,它通常超级简单而且只有 1 行短...

              NSIndexPath *indexPath = [tableView indexPathForCell:(UITableViewCell *)[[sender superview] superview]];
          

          就是这样,如果嵌套更多,添加更多超级视图。

          更多信息:

          您所做的只是要求父视图及其作为单元格的父视图。然后你向你的 tableview 询问你刚刚得到的那个单元格的索引路径。

          【讨论】:

          • 这是迄今为止最好的解决方案。我见过其他解决方案在回收电池时失败
          • 在 XCode 5 / SDK7 下使用接口 Builder 创建单元格(作为“原型”),您会遇到一个问题,即在 iOS6 和 iOS7 设备上,嵌入式控件嵌套在不同的子视图级别。严重地。我是这样做的,但不得不更改为“命中位置”hack。
          【解决方案8】:

          如果您将tag 属性设置为行号(如其他答案所建议的那样),您必须每次在tableView:cellForRowAtIndexPath: 中更新它(因为一个单元格可以重复用于不同的行)。

          相反,当您需要行号时,您可以沿着superview 链从UISwitch(或任何其他视图)到UITableViewCell,然后再到UITableView,并询问表查看单元格的索引路径:

          static NSIndexPath *indexPathForView(UIView *view) {
              while (view && ![view isKindOfClass:[UITableViewCell class]])
                  view = view.superview;
              if (!view)
                  return nil;
              UITableViewCell *cell = (UITableViewCell *)view;
              while (view && ![view isKindOfClass:[UITableView class]])
                  view = view.superview;
              if (!view)
                  return nil;
              UITableView *tableView = (UITableView *)view;
              return [tableView indexPathForCell:cell];
          }
          

          这不需要tableView:cellForRowAtIndexPath: 中的任何内容。

          【讨论】:

          • 我同意标签,但您的解决方案似乎有点复杂。你能看出我提出的方法有什么缺点吗?似乎有很多丑陋的黑客可以解决这个常见问题,我对我的很满意,但我没有看到它在其他任何地方使用!
          • @jrturton 这是一个有趣的方法。如果UISwitch 的来源恰好位于其单元格之外,它将失败,这当然不太可能。
          • 是的,在那种情况下很难点击!
          • 如果原点在(-1,0),大部分还是可见可触摸的。
          • 我认为这里的代码通过第二个循环寻找表格视图让潜在买家望而却步。调用者在调用时最好知道表视图,否则他会想知道应该将结果路径应用到哪个表。这是一个非常好的 tableview 扩展。我认为这种方法在这里被错误地贬低为相对于 SDK 更改不安全。它和 UIKit、IMO 中的任何东西一样安全——而且(可能,虽然可能微不足道,所以谁在乎)计算量比公认的解决方案少。 (我添加了一个答案来表达我的观点)
          【解决方案9】:

          执行此操作的一种常见方法是将控件(在您的情况下为开关)的tag 设置为可用于识别行或表示的对象的内容。

          例如,在tableView:cellForRowAtIndexPath: 中,将开关的tag 属性设置为indexPath.row,然后在您的操作方法中,您可以从发件人那里获取标签。

          就个人而言,我不喜欢这种方法,更喜欢继承 UITableViewCell。 此外,最好在标签上添加一个“偏移量”,以防止与其他视图的标签发生任何冲突。

          【讨论】:

            【解决方案10】:

            cellForRowAtIndexPath: 中,将控件的tag 属性设置为indexPath.row

            【讨论】:

            • 这适用于您只需要知道行或节的情况,但如果您同时需要两者,则变得更加复杂
            • 没有。插入或删除行时标签不会更新
            猜你喜欢
            • 2014-02-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-04-20
            • 2012-06-01
            • 1970-01-01
            • 2013-05-13
            相关资源
            最近更新 更多