如何从单元格获取UITableViewCell的indexPath?

80

如何从单元格中获取其在 UITableView 中的 indexPath

我在 Stack Overflow 和 Google 上搜索过,但所有信息都是相反的。是否有一种方法可以访问 superView/UITableView,然后搜索单元格?

关于上下文的更多信息:我有两个类,一个叫做 Cue,另一个叫做 CueTableCell(它是 UITableViewCell 的子类)。CueTableCellCue 的视觉表示(两个类都有互相的指针)。Cue 对象在链表中,并且当用户执行某个命令时,需要选择下一个 Cue 的可视化表示形式(即 CueTableCell)。因此,Cue 类调用列表中下一个 Cueselect 方法,该方法从 cell 中检索 UITableView 并调用其 selectRowAtIndexPath:animated:scrollPosition:,其中需要 UITableViewCellindexPath


2
就像你之前关于从单元格获取UITableView的问题一样 - 为什么要这样做?这似乎是一个糟糕设计决策的代码异味 - 对于单元格知道它的indexPath或者它是否在屏幕上,没有太多合法的用例。 - Paul.s
我同意Paul.s的评论。你应该说明为什么想要这样做,因为你试图做的可能是一个坏主意。 - rdelmar
@Paul.s,我在问题中发布了我的程序设计。这可能是不好的编程实践,如果您能提出替代方案,那将非常有帮助。我对Cocoa非常陌生,对编程也相对较新。 - sinθ
值得注意的是,存在indexPathsForVisibleRows - 当您想要知道滚动时的“位置”时,这非常关键。 - Fattie
12个回答

143
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];

阅读UITableView文档对于帮助理解是有益的,即使这被一些人认为是有争议的(请参见下面的评论)。

单元格不应该知道它的索引路径是什么。 控制器应该包含任何基于数据模型操作UI元素或基于UI交互修改数据的代码。 (请参见MVC)。


43
如果您阅读问题会更有帮助。这句话来自UITableViewCell,它没有tableView属性(并且据所有报道,也不应该有)。 - voidref
15
@voidref 这也有助于思考这个问题;-)。该单元格无需知道其索引路径,因此此代码应放在控制器中。有关MVC的背景,请参阅Cocoa 核心竞争力 - Mundi
2
无论从一开始就显而易见,人们都需要正确的答案。 - jdog
11
没有回答原问题。有时候,一个单元格需要知道它所在的路径。例如,如果您在子类化的单元格上实现了某种滑动操作,您可能需要通知父视图当前正在滑动哪个单元格。一种方法是让parentView中的一个方法循环遍历tableView单元格并检查已在单元格中设置的iVar。更好的方法是通过委托方法让单元格告诉parentView其indexPath...因此,单元格需要知道它的indexPath。所以,Mundi,请先回答问题,然后再发表您的观点。 - wuf810
5
索引路径是单元格和数据模型之间的“粘合剂”,因此它属于控制器。上面的长注释事实上是错误的。@wuf810... 因为控制器知道单元格和索引路径之间的关系,而单元格不知道(也不应该知道)。 - Mundi
显示剩余7条评论

25

您可以从UITableViewCell中尝试使用以下代码:

NSIndexPath *indexPath = [(UITableView *)self.superview indexPathForCell: self];

编辑:似乎在iOS 8.1.2及更高版本上无法使用。感谢 Chris Prince 指出。


7
在 iOS 8.1.2 中,表格视图单元格的父视图属于 UITableViewWrapperView 类型。这个父视图的上一级是表格视图。但我认为我们在这里涉险滑坡了… - Chris Prince
2
一切都回到“单元格不应该知道其索引路径”的论点。几乎总是访问superview是一种hack,而且不足为奇的是它在一段时间后不起作用。 - fatuhoku
完全正确的fatuhoku,这不是单元格知道索引路径的问题。 - Yahya Alshaar

11
  1. 在单元格的 .h 文件中添加一个弱引用的 tableView 属性:

    @property (weak,nonatomic)UITableView *tableView;

  2. 在 cellForRowAtIndex 方法中分配该属性:

    cell.tableView = tableView;

  3. 现在,每当需要单元格的 indexPath 时:

    NSIndexPath *indexPath = [cell.tableView indexPathForCell:cell];


这是可行的,因为我们只存储了指向tableView本身的指针,但是,indexPathForCell可靠吗?我认为它只提供可见单元格的indexPath,对吗? - jcpennypincher

11
您可以尝试这个简单的提示:
在您的UITableViewCell类上:
@property NSInteger myCellIndex; //or add directly your indexPath 

并且在你的 cellForRowAtIndexPath 方法中:

...
[cell setMyCellIndex : indexPath.row]

现在,您可以在任何地方检索您的单元格索引


5
重新排序或删除单元格后,您需要记得手动更新此信息。 - konrad

10

针对那些说“这是一个坏主意”的人,我的情况是这样的:我在UITableViewCell上有一个按钮,当按下它时,它会转到另一个视图。由于这不是单元格本身的选择,所以[self.tableView indexPathForSelectedRow]不能使用。

这给了我两个选择:

  1. 将我需要传递到视图中的对象存储在表格单元格本身中。虽然这可以工作,但这将违背我拥有NSFetchedResultsController的目的,因为我不想将所有对象存储在内存中,特别是如果表格很长。
  2. 使用索引路径从获取控制器检索项目。是的,看起来很丑陋,因为我必须通过一种hack方法来找出NSIndexPath,但它最终比在内存中存储对象要便宜得多。

indexPathForCell:是正确的方法,但以下是我将如何实施它(假设此代码是在UITableViewCell的子类中实现的):

// uses the indexPathForCell to return the indexPath for itself
- (NSIndexPath *)getIndexPath {
    return [[self getTableView] indexPathForCell:self];
}

// retrieve the table view from self   
- (UITableView *)getTableView {
    // get the superview of this class, note the camel-case V to differentiate
    // from the class' superview property.
    UIView *superView = self.superview;

    /*
      check to see that *superView != nil* (if it is then we've walked up the
      entire chain of views without finding a UITableView object) and whether
      the superView is a UITableView.
    */
    while (superView && ![superView isKindOfClass:[UITableView class]]) {
        superView = superView.superview;
    }

    // if superView != nil, then it means we found the UITableView that contains
    // the cell.
    if (superView) {
        // cast the object and return
        return (UITableView *)superView;
    }

    // we did not find any UITableView
    return nil;
}

附言:我的真实代码确实可以从表视图中访问所有这些内容,但我正在提供一个示例,说明为什么有人可能想要直接在表格单元格中执行此类操作。


8

对于Swift

let indexPath :NSIndexPath? = (self.superview.superview as! UITableView)?.indexPathForCell(self)

4
在iOS 11.0上,您可以使用UITableView.indexPathForRow(at point: CGPoint) -> IndexPath?
if let indexPath = tableView.indexPathForRow(at: cell.center) {
    tableView.selectRow
    (at: indexPath, animated: true, scrollPosition: .middle)
}

它获取单元格的中心点,然后tableView返回与该点对应的indexPath。


4
这个问题的答案实际上对我很有帮助。
我使用了 NSIndexPath *indexPath = [self.tableView indexPathForCell:sender]; 在我的情况下,sender 是一个 UITableViewCell ,来自于 prepareForSegue 方法。
我之所以使用这个方法,是因为我没有 TableViewController ,但我有一个 UITableView property outlet
我需要找到 Cell 的标题,因此需要知道它的 indexPath。
希望这可以帮助任何人!

2

Swift 3.0及以上版本-

如果需要从自定义单元格内部获取indexPath-

if let tableView = self.superview as? UITableView{
    if let indexPath = tableView.indexPath(for: self){
        print("Indexpath acquired.")
    }else{
        print("Indexpath could not be acquired from tableview.")
    }
}else{
    print("Superview couldn't be cast as tableview")
}

最佳实践是要注意失败案例。


1

尝试这个(只有在tableView仅有一个部分且所有单元格高度相等的情况下才能生效):

//get current cell rectangle relative to its superview
CGRect r = [self convertRect:self.frame toView:self.superview];

//get cell index
int index = r.origin.y / r.size.height;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接