How to get UITableView from UITableViewCell?
Solution 1
To avoid checking the iOS version, iteratively walk up the superviews from the cell's view until a UITableView is found:
Objective-C
id view = [cellInstance superview];
while (view && [view isKindOfClass:[UITableView class]] == NO) {
view = [view superview];
}
UITableView *tableView = (UITableView *)view;
Swift
var view = cellInstance.superview
while (view != nil && (view as? UITableView) == nil) {
view = view?.superview
}
if let tableView = view as? UITableView {
tableView.beginUpdates()
tableView.endUpdates()
}
Solution 2
In iOS7 beta 5 UITableViewWrapperView
is the superview of a UITableViewCell
. Also UITableView
is superview of a UITableViewWrapperView
.
So for iOS 7 the solution is
UITableView *tableView = (UITableView *)cell.superview.superview;
So for iOSes up to iOS 6 the solution is
UITableView *tableView = (UITableView *)cell.superview;
Solution 3
Swift 5 extension
Recursively
extension UIView {
func parentView<T: UIView>(of type: T.Type) -> T? {
guard let view = superview else {
return nil
}
return (view as? T) ?? view.parentView(of: T.self)
}
}
extension UITableViewCell {
var tableView: UITableView? {
return parentView(of: UITableView.self)
}
}
Using loop
extension UITableViewCell {
var tableView: UITableView? {
var view = superview
while let v = view, v.isKind(of: UITableView.self) == false {
view = v.superview
}
return view as? UITableView
}
}
Solution 4
Before iOS7, the cell's superview was the UITableView
that contained it. As of iOS7 GM (so presumably will be in the public release as well) the cell's superview is a UITableViewWrapperView
with its superview being the UITableView
. There are two solutions to the problem.
Solution #1: Create a UITableViewCell
category
@implementation UITableViewCell (RelatedTable)
- (UITableView *)relatedTable
{
if ([self.superview isKindOfClass:[UITableView class]])
return (UITableView *)self.superview;
else if ([self.superview.superview isKindOfClass:[UITableView class]])
return (UITableView *)self.superview.superview;
else
{
NSAssert(NO, @"UITableView shall always be found.");
return nil;
}
}
@end
This is a good drop-in replacement to using cell.superview
, makes it easy to refactor your existing code -- just search and replace with [cell relatedTable]
, and throw in an assert to ensure that if the view hierarchy changes or reverts in the future it will show up immediately in your tests.
Solution #2: Add a Weak UITableView
reference to UITableViewCell
@interface SOUITableViewCell
@property (weak, nonatomic) UITableView *tableView;
@end
This is a much better design, though it will require a bit more code refactoring to use in existing projects. In your tableView:cellForRowAtIndexPath
use SOUITableViewCell as your cell class or make sure your custom cell class is subclassed from SOUITableViewCell
and assign the tableView to the cell's tableView property. Inside the cell you can then refer to the containing tableview using self.tableView
.
Solution 5
Whatever you may end up managing to do by calling super view or via the responder chain is going to be very fragile. The best way to do this, if the cells wants to know something, is to pass an object to the cell that responds to some method that answers the question the cell wants to ask, and have the controller implement the logic of determining what to answer (from your question I guess the cell wants to know if something is visible or not).
Create a delegate protocol in the cell, set the delegate of the cell the tableViewController and move all the ui "controlling" logic in the tableViewCotroller.
The table view cells should be dum view that will only display information.
Related videos on Youtube
sinθ
Updated on December 16, 2021Comments
-
sinθ over 2 years
I have a
UITableViewCell
which is linked to an object and I need to tell if the cell is visible. From the research I've done, this means I need to somehow access theUITableView
that contains it (from there, there are several ways to check if it's visible). So I'm wondering ifUITableViewCell
has a pointer to theUITableView
, or if there was any other way to get a pointer from the cell?-
max_ about 11 yearsWhat's the purpose of this?
-
Chris Loonam about 11 years
[cell superView]
maybe? -
Paul.s about 11 yearsIt's worth explaining why you think you need this - as this may be a sign of bad design as I cannot really think of many legitimate reasons for a cell to know if it is on screen or not.
-
chadbag over 9 years@Paul.s We have a gesture recognizer on an image in a cell and when the cell is touched, it opens up another overlay view, think popover style, that should overlay as many cells as needed to display properly. For this to work it needs the TableView or other view given to it to display in. Not really happy with the solutions but to get the effect desired getting the UITableView of the UITableViewCell is the best we have come up with.
-
PJ_Finnegan over 6 years@chadbag Can't you capture the reference to the UITableView in the IBAction/closure you use as the image touch event handler? Usually this is done in a UITableViewController which should be aware of the table view instance.
-
chadbag over 6 years@PJ_Finnegan That was 2 1/2 years ago at a former job. I don't remember what we were doing or why. Sorry.
-
PJ_Finnegan over 6 years@chadbag no worries, hopefully I gave an idea to someone else with the same problem.
-
-
Gabe almost 11 yearsThis is no longer true in iOS7, In iOS7 beta5 UITableViewWrapperView is the superview of a UITableViewCell... causing me issues right now
-
Hermann Klecker almost 11 yearsThanks for the comment. I'll have to check some of my code then.
-
Ryan Romanchuk almost 11 yearsOh man, this is a brutal API change. What's apple's best practice for branching if you support both 6 and 7?
-
memmons almost 11 years@RyanRomanchuk Here is one good suggestion: devforums.apple.com/message/865550#865550 -- create a weak pointer to your related tableView when the cell is created. Alternately, create a
UITableViewCell
category with a new method,relatedTableView
which does a check for an iOS version and returns the appropriate superView. -
Steven Fisher over 10 yearsWrite a recursive category on UIView for this. You don't need to check the version; just call superview until you find a table view cell or the top of the view stack. This is what I used in iOS 6, and it worked without modification in iOS 7. And should work sitll in iOS 8.
-
Steven Fisher over 10 yearsBeg pardon; I meant until you find the table view. :)
-
Cristi Băluță over 10 yearsMaybe even better through delegates. i'm temporarily using a passed reference to the table since the parent gave me problems.
-
Javier Soto over 10 yearsWhat I described is basically using the delegate pattern :)
-
devios1 about 10 yearsI disagree that solution 2 is a better design. It has more points of failure and requires disciplined manual intervention. The first solution is actually quite reliable, though I would implement it as @idris suggested in his answer for a bit more futureproofing.
-
CopperCash about 10 yearsTest
[self.superview.superview isKindOfClass:[UITableView class]]
should be the first, 'cause the iOS 7 is more and more. -
JaredH about 10 yearsThis will not work, as the immediate superview of a UITableViewCell is not a UITableView. As of iOS 7, the UITableViewCell superview is a UITableViewWrapperView. See the other answers in here for less fragile, more reliable approaches.
-
djskinner over 9 yearsThanks. It would appear that this has changed once again in iOS 8 and this takes care of all versions nicely.
-
Cenny about 9 yearsI would recommend adding a weak reference to the tableview to the cell to avoid compatibility issues in future updates.
-
RomanN over 8 yearsRather a weak way. It won't work if view's hierarchy changes in future.
-
kabiroberai almost 8 yearsYou can compact the entire
lookForSuperviewOfType:
method body into one line, making it even swift-ey:return superview as? T ?? superview?.superviewOfType(type)
-
Mehdzor almost 8 years@kabiroberai, thank you. I added your advice to the answer.
-
robert over 7 yearsThe name used by the recursive call needs to match. So this needs to read: ... ?? superview?.lookForSuperviewOfType(type)
-
Alejandro Iván over 7 yearsIt would be better to simply create a weak property that actually holds a pointer to the tableview. In the subclass
@property (weak, nonatomic) UITableView *tableView;
and intableView:cellForRowAtIndexPath:
just setcell.tableView = tableView;
. -
Antonio Nunes over 7 yearsThis will loop forever if the cell is not yet in the table view when called. To fix, add a check for nil:
for (view = self.superview; view && ![view isKindOfClass:UITableView.class]; view = view.superview);
-
Jonny almost 6 yearsCurrent experience is that after dequeuing a cell, a tableview is not appearing as a superview of the cell right away - if I scroll the cell out of view and back in again I do find a table view as a superview (recursive search as described in other answers). Autolayout and possibly other factors are likely reasons.
-
Alex Terente over 5 yearsThis should be the accepted answer, people see this question, see the 150+ upvote answer and they think is fine to get the tableView from the cell and even more maybe keep a strong reference to it unfortunately.
-
thibaut noah almost 5 yearsA rule of thumb is to not use force unwrapping
-
Orkhan Alikhanov almost 5 years@thibautnoah There is a
view != nil
check before unwrapping. Updated to cleaner code though