How to get UITableViewCell indexPath from the Cell?
IphoneIosObjective CXcodeCocoaIphone Problem Overview
How do I, from a cell, get its indexPath
in a UITableView
?
I've searched around stack overflow and google, but all the information is on the other way around. Is there some way to access the superView
/UITableView
and then search for the cell?
More information about the context: there are two classes that I have, one is called Cue
and one is called CueTableCell
(which is a subclass of UITableViewCell
) CueTableCell
is the visual representation of Cue
(both classes have pointers to each other). Cue
objects are in a linked list and when the user performs a certain command, the visual representation (the CueTableCell
) of the next Cue
needs to be selected. So the Cue
class calls the select
method on the next Cue
in the list, which retrieves the UITableView
from the cell
and calls its selectRowAtIndexPath:animated:scrollPosition:
, for which it needs the indexPath
of the UITableViewCell
.
Iphone Solutions
Solution 1 - Iphone
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
It helps reading the UITableView documentation, even if this is by some regarded to be controversial (see comments below).
The cell has no business knowing what its index path is. The controller should contain any code that manipulates UI elements based on the data model or that modifies data based on UI interactions. (See MVC.)
Solution 2 - Iphone
Try with this from your UITableViewCell:
NSIndexPath *indexPath = [(UITableView *)self.superview indexPathForCell: self];
EDIT: Seems this doesn't work on iOS 8.1.2 and further. Thanks to Chris Prince for pointing it.
Solution 3 - Iphone
-
Put a weak tableView property in cell's .h file like:
@property (weak,nonatomic)UITableView *tableView;
-
Assign the property in cellForRowAtIndex method like:
cell.tableView = tableView;
-
Now wherever you need the indexPath of cell:
NSIndexPath *indexPath = [cell.tableView indexPathForCell:cell];
Solution 4 - Iphone
You can try this simple tip:
on your UITableViewCell
Class :
@property NSInteger myCellIndex; //or add directly your indexPath
and on your cellForRowAtIndexPath
method :
...
[cell setMyCellIndex : indexPath.row]
now, you can retrieve your cell index anywhere
Solution 5 - Iphone
To address those who say "this is a bad idea", in my case, my need for this is that I have a button on my UITableViewCell
that, when pressed, is a segue to another view. Since this is not a selection on the cell itself, [self.tableView indexPathForSelectedRow]
does not work.
This leaves me two options:
- Store the object that I need to pass into the view in the table cell itself. While this would work, it would defeat the point of me having an
NSFetchedResultsController
because I do not want to store all the objects in memory, especially if the table is long. - Retrieve the item from the fetch controller using the index path. Yes, it seems ugly that I have to go figure out the
NSIndexPath
by a hack, but it's ultimately less expensive than storing objects in memory.
indexPathForCell:
is the correct method to use, but here's how I would do it (this code is assumed to be implemented in a subclass of 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;
}
P.S. My real code does access all this from the table view, but I'm giving an example of why someone might want to do something like this in the table cell directly.
Solution 6 - Iphone
For swift
let indexPath :NSIndexPath? = (self.superview.superview as! UITableView)?.indexPathForCell(self)
Solution 7 - Iphone
The answer to this question actually helped me a lot.
I used NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
The sender
in my case was a UITableViewCell
and comes from the prepareForSegue
method.
I used this because I did not have a TableViewController
but i had UITableView property outlet
I needed to find out the title of the Cell
and hence needed to know the indexPath of it.
Hope this helps anyone!
Solution 8 - Iphone
On iOS 11.0, you can use UITableView.indexPathForRow(at point: CGPoint) -> IndexPath?
:
if let indexPath = tableView.indexPathForRow(at: cell.center) {
tableView.selectRow
(at: indexPath, animated: true, scrollPosition: .middle)
}
It gets the center point of the cell and then the tableView returns the indexPath corresponding to that point.
Solution 9 - Iphone
try this (it only works if the tableView has only one section and all cells has equal heights) :
//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;
Solution 10 - Iphone
Swift 3.0 and above-
If one needs to get the indexpath from within a custom cell-
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")
}
It's good practice is to look out for the failure cases.
Solution 11 - Iphone
Try with this from your UITableViewCell:
NSIndexPath *indexPath = [(UITableView *)self.superview.superview indexPathForCell:self];
Solution 12 - Iphone
>Swift 4.x
To provide access to my cell, i did something like this:
I have an extension of the UIView:
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
And then.. in my cell Class:
class SomeCell: UITableViewCell {
func someMethod() {
if let myViewController = self.parentViewController as? CarteleraTableViewController {
let indexPath : IndexPath = (myViewController.tableView).indexPath(for: self)!
print(indexPath)
}
}
}
Thats all form me.
Best regards.