如何从 UITableViewCell 获取 UITableView?

我有一个 UITableViewCell链接到一个对象,我需要告诉如果细胞是可见的。根据我所做的研究,这意味着我需要以某种方式访问包含它的 UITableView(从那里,有几种方法来检查它是否可见)。所以我想知道 UITableViewCell是否有一个指向 UITableView的指针,或者是否有其他方法从单元格中获得指针?

71536 次浏览

If it is visible then it has a superview. And ... surprise ... the superview is an UITableView object.

However, having a superview is no guarantee for being on screen. But UITableView provides methods to determine which cells are visible.

And no, there is no dedicated reference from a cell to a table. But when you subclass UITableViewCell you may introduce one and set it upon creation. (I did that myself a lot before I thought of the subview hierarchy.)

Update for iOS7: Apple has changed the subview hierarchy here. As usual when working with things that are not detailled documented, there is always a risk that things change. It is far saver to "crawl up" the view hierarchy until a UITableView object is eventually found.

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.

this code `UITableView *tblView=[cell superview]; will give you an instance of the UItableview which contains the tabe view cell

I based this solution on Gabe's suggestion that UITableViewWrapperView object is the superview of UITableViewCell object in iOS7 beta5 .

Subclass UITableviewCell :

- (UITableView *)superTableView
{
return (UITableView *)[self findTableView:self];
}


- (UIView *)findTableView:(UIView *)view
{
if (view.superview && [view.superview isKindOfClass:[UITableView class]]) {
return view.superview;
}
return [self findTableView:view.superview];
}

In iOS7 beta 5 UITableViewWrapperView is the superview of a UITableViewCell. Also ABC2 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;

I suggest you traverse the view hierarchy this way to find the parent UITableView:

- (UITableView *) findParentTableView:(UITableViewCell *) cell
{
UIView *view = cell;
while ( view && ![view isKindOfClass:[UITableView class]] )
{
#ifdef DEBUG
NSLog( @"%@", [[view  class ] description] );
#endif
view = [view superview];
}


return ( (UITableView *) view );
}

Otherwise your code will break when Apple changes the view hierarchy again.

Another answer that also traverses the hierarchy is recursive.

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()
}

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.

I created a category on UITableViewCell to get its parent tableView:

@implementation UITableViewCell (ParentTableView)




- (UITableView *)parentTableView {
UITableView *tableView = nil;
UIView *view = self;
while(view != nil) {
if([view isKindOfClass:[UITableView class]]) {
tableView = (UITableView *)view;
break;
}
view = [view superview];
}
return tableView;
}




@end

Best,

Instead of superview, try using ["UItableViewvariable" visibleCells].

I used that in a foreach loops to loop through the cells that the app saw and it worked.

for (UITableView *v in [orderItemTableView visibleCells])//visibleCell is the fix.
{
@try{
[orderItemTableView reloadData];
if ([v isKindOfClass:[UIView class]]) {
ReviewOrderTableViewCell *cell = (ReviewOrderTableViewCell *)v;
if (([[cell deleteRecord] intValue] == 1) || ([[[cell editQuantityText] text] intValue] == 0))
//code here
}
}
}

Works like a charm.

UITableViewCell Internal View Hierarchy Change in iOS 7


Using iOS 6.1 SDK


<UITableViewCell>
| <UITableViewCellContentView>
|    | <UILabel>


Using iOS 7 SDK


<UITableViewCell>
| <UITableViewCellScrollView>
|    | <UIButton>
|    |    | <UIImageView>
|    | <UITableViewCellContentView>
|    |    | <UILabel>




The new private UITableViewCellScrollView class is a subclass of UIScrollView and is what allows this interaction:




![enter image description here][1]




[1]: http://i.stack.imgur.com/C2uJa.gif

http://www.curiousfind.com/blog/646 Thank You

I Borrowed and modified a little bit from the above answer and come up with the following snippet.

- (id)recursivelyFindSuperViewWithClass:(Class)clazz fromView:(id)containedView {
id containingView = [containedView superview];
while (containingView && ![containingView isKindOfClass:[clazz class]]) {
containingView = [containingView superview];
}
return containingView;
}

Passing in class offers the flexibility for traversing and getting views other than UITableView in some other occasions.

My solution to this problem is somewhat similar to other solutions, but uses an elegant for-loop and is short. It should also be future-proof:

- (UITableView *)tableView
{
UIView *view;
for (view = self.superview; ![view isKindOfClass:UITableView.class]; view = view.superview);
return (UITableView *)view;
}
UITableView *tv = (UITableView *) self.superview.superview;
BuyListController *vc = (BuyListController *) tv.dataSource;

Here is the Swift version based on above answers. I have generalized into ExtendedCell for later usage.

import Foundation
import UIKit


class ExtendedCell: UITableViewCell {


weak var _tableView: UITableView!


func rowIndex() -> Int {
if _tableView == nil {
_tableView = tableView()
}


return _tableView.indexPathForSelectedRow!.row
}


func tableView() -> UITableView! {
if _tableView != nil {
return _tableView
}


var view = self.superview
while view != nil && !(view?.isKindOfClass(UITableView))! {
view = view?.superview
}


self._tableView = view as! UITableView
return _tableView
}
}

Hope this help :)

You can get it with one line of code.

UITableView *tableView = (UITableView *)[[cell superview] superview];

Swift 2.2 solution.

An extension for UIView that recursively searches for a view with a specific type.

import UIKit


extension UIView {
func lookForSuperviewOfType<T: UIView>(type: T.Type) -> T? {
guard let view = self.superview as? T else {
return self.superview?.lookForSuperviewOfType(type)
}
return view
}
}

or more compact (thanks to kabiroberai):

import UIKit


extension UIView {
func lookForSuperviewOfType<T: UIView>(type: T.Type) -> T? {
return superview as? T ?? superview?.superviewOfType(type)
}
}

In your cell you just call it:

let tableView = self.lookForSuperviewOfType(UITableView)
// Here we go

Mind that UITableViewCell is added on the UITableView only after cellForRowAtIndexPath execution.

extension UIView {
func parentTableView() -> UITableView? {
var viewOrNil: UIView? = self
while let view = viewOrNil {
if let tableView = view as? UITableView {
return tableView
}
viewOrNil = view.superview
}
return nil
}
}

Minimally tested but this non-generic Swift 3 example seems to work:

extension UITableViewCell {
func tableView() -> UITableView? {
var currentView: UIView = self
while let superView = currentView.superview {
if superView is UITableView {
return (superView as! UITableView)
}
currentView = superView
}
return nil
}
}

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
}
}

from @idris answer I wrote an expansion for UITableViewCell in Swift

extension UITableViewCell {
func relatedTableView() -> UITableView? {
var view = self.superview
while view != nil && !(view is UITableView) {
view = view?.superview
}


guard let tableView = view as? UITableView else { return nil }
return tableView
}