如何从UITableViewCell获取UITableView?


100

我有一个UITableViewCell链接到对象的对象,我需要确定单元格是否可见。根据我已经完成的研究,这意味着我需要以某种方式访问UITableView包含它的对象(从那里,有几种方法可以检查它是否可见)。所以我想知道是否UITableViewCell有指向的指针UITableView,或者是否还有其他方法可以从单元格中获取指针?


2
目的是什么?
max_

[cell superView]也许?
克里斯·卢南

6
值得解释为什么您认为需要这样做-因为这可能是不良设计的标志,因为我无法真正想到使单元知道其是否在屏幕上的许多合理原因。
Paul.s

@ Paul.s我们在一个单元格中的图像上有一个手势识别器,当触摸该单元格时,它会打开另一个覆盖视图,即弹出式样式,该视图应覆盖所需数量的单元格以正确显示。为此,它需要显示TableView或提供给它的其他视图。对于解决方案并不是很满意,但要获得理想的效果,最好的方法就是获得UITableViewCell的UITableView。
chadbag 2015年

1
@chadbag不用担心,希望我给遇到相同问题的其他人一个主意。
PJ_Finnegan's

Answers:


153

为了避免检查iOS版本,请从单元格视图迭代遍历超级视图,直到找到UITableView为止:

id view = [tableViewCellInstance superview];

while (view && [view isKindOfClass:[UITableView class]] == NO) {
    view = [view superview]; 
}

UITableView *tableView = (UITableView *)view;

1
谢谢。看来这在iOS 8中再次发生了变化,可以很好地处理所有版本。
djskinner 2014年

2
我建议为单元格添加对表视图的弱引用,以避免将来更新中出现兼容性问题。
Cenny

1
而是一种较弱的方法。如果将来视图的层次结构发生更改,它将无法正常工作。
RomanN 2015年

2
最好只创建一个实际上包含指向表视图的指针的弱属性。在子类中@property (weak, nonatomic) UITableView *tableView;并在tableView:cellForRowAtIndexPath:set中cell.tableView = tableView;
亚历杭德罗·伊万

当前的经验是,在使单元出队后,表视图不会立即显示为该单元的超级视图-如果将单元格滚动出视图并再次返回,则确实会找到一个表视图作为超级视图(如其他答案)。自动布局和其他可能的因素可能是原因。
强尼

48

在iOS7 beta 5中UITableViewWrapperView是的超级视图UITableViewCell也是UITableView一个的监督UITableViewWrapperView

因此,对于iOS 7,解决方案是

UITableView *tableView = (UITableView *)cell.superview.superview;

因此,对于iOS 6以下的iOS,解决方案是

UITableView *tableView = (UITableView *)cell.superview;


5
噢,天哪,这是残酷的API更改。如果同时支持6和7,那么苹果进行分支的最佳实践是什么?
Ryan Romanchuk 2013年

@RyanRomanchuk这是一个很好的建议:devforums.apple.com/message/865550#865550-创建单元格时,创建指向相关tableView的弱指针。或者,UITableViewCell使用新方法创建类别,该方法relatedTableView将检查iOS版本并返回适当的superView。
记忆

3
为此,在UIView上编写一个递归类别。您无需检查版本;只需调用superview,直到找到一个表视图单元格或视图堆栈的顶部。这是我在iOS 6中使用,并且无需修改iOS中7工作,应该工作罗纳尔多,也很想在iOS的8
史蒂芬费舍尔

请原谅; 我的意思是直到找到表格视图。:)
史蒂芬·费舍尔

39

Swift 5扩展

递归地

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

使用循环

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

经验法则是不要使用武力解包
thibaut noah

1
@thibautnoah view != nil展开包装前先检查一下。尽管已更新为更清洁的代码
Orkhan Alikhanov

25

在iOS7之前,单元的超级视图就是其中的超级视图UITableView。从iOS7 GM开始(大概也将在公共发行版中),该单元的超级视图为UITableViewWrapperView,其超级视图为UITableView。有两种解决方案。

解决方案1:创建UITableViewCell类别

@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

这是使用的很好的直接替代品cell.superview,可以轻松地重构现有代码-只需搜索并替换为[cell relatedTable],然后抛出一个断言,以确保如果视图层次结构将来发生更改或还原,它将立即显示出来。在您的测试中。

解决方案2:将弱UITableView引用添加到UITableViewCell

@interface SOUITableViewCell

   @property (weak, nonatomic) UITableView *tableView;

@end

这是一个更好的设计,尽管需要更多的代码重构才能在现有项目中使用。在tableView:cellForRowAtIndexPath使用SOUITableViewCell作为单元格类时,或确保自定义单元格类是该类的子类,SOUITableViewCell并将tableView分配给该单元格的tableView属性。然后,您可以在单元格内部使用引用包含的tableview self.tableView


我不同意解决方案2是更好的设计。它具有更多的故障点,需要有纪律的人工干预。第一个解决方案实际上是相当可靠的,尽管我会按照@idris在他的回答中建议的那样实现它,以提供更多的未来证明。
devios1 2014年

1
测试[self.superview.superview isKindOfClass:[UITableView class]]应该是第一个,因为iOS 7越来越多。
CopperCash 2014年

7

如果可见,则具有超级视图。而且...令人惊讶...该超级视图是一个UITableView对象。

但是,拥有超级视图并不能保证能够在屏幕上显示。但是UITableView提供了确定哪些单元格可见的方法。

不,没有从单元格到表的专用引用。但是,当您继承UITableViewCell的子类时,可以引入一个并在创建时进行设置。(在想到子视图层次结构之前,我做了很多自己的事情。)

iOS7更新: Apple在这里更改了子视图层次结构。像往常一样,当处理未详细记录的内容时,始终存在更改的风险。“爬网”视图层次结构,直到最终找到UITableView对象,这是省钱的。


14
在iOS7中不再是这样,在iOS7 beta5中,UITableViewWrapperView是UITableViewCell的超级视图...引起我现在的问题
Gabe

感谢您的评论。然后,我必须检查一些代码。
Hermann Klecker 2013年

7

Swift 2.2解决方案。

UIView的扩展,以递归方式搜索特定类型的视图。

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

或更紧凑(感谢karobroberai):

import UIKit

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

在您的单元格中,您只需称呼它:

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

请注意,只有在执行cellForRowAtIndexPath之后,才将UITableViewCell添加到UITableView上。


您可以将整个lookForSuperviewOfType:方法主体压缩为一行,甚至可以快速查看:return superview as? T ?? superview?.superviewOfType(type)
kabiroberai

@kabiroberai,谢谢。我将您的建议添加到了答案中。
Mehdzor

递归调用使用的名称必须匹配。因此,这需要阅读:... ?? superview?.lookForSuperviewOfType(type)
罗伯特

7

通过调用超级视图或通过响应程序链最终可能要做的任何事情都将非常脆弱。如果单元格想知道某事,最好的方法是将一个对象传递给该单元格,该对象对可以回答该单元格要问的问题的某种方法做出响应,并使控制器执行确定要回答什么的逻辑(从您的问题中我猜想该单元想知道是否可见)。

在单元中创建委托协议,将单元的委托设置为tableViewController,并将所有ui“控制”逻辑移到tableViewCotroller中。

表格视图单元格应为仅显示信息的dum视图。


通过代表也许更好。由于父级给我带来了问题,我暂时使用了对表的传递引用。
Cristi Băluță

1
我所描述的基本上是使用委托模式:)
Javier Soto 2014年

这应该是公认的答案,人们看到此问题,看到150+赞成答案,并且他们认为从单元格中获取tableView是很好的,而且不幸的是,它可能仍然是对此的强有力的参考。
亚历克斯·特伦特

6

我在UITableViewCell上创建了一个类别以获取其父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

最好,


3

这是基于以上答案的Swift版本。我已将其概括ExtendedCell为以后使用。

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

希望这个帮助:)


2

我基于Gabe的建议(该UITableViewWrapperView对象是UITableViewCelliOS7 beta5 中对象的超级视图) 提出了该解决方案。

子类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];
}

2

我从以上答案中借用并修改了一些内容,并提出了以下代码段。

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

在某些情况下,通过类传递可以灵活地遍历和获取UITableView以外的视图。


2

我对这个问题的解决方案与其他解决方案有些相似,但是使用了优雅的for循环,而且简短。它也应该是面向未来的:

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

如果在调用时该单元格尚未在表视图中,则它将永远循环。要修复,请添加nil支票: for (view = self.superview; view && ![view isKindOfClass:UITableView.class]; view = view.superview);
Antonio Nunes

2
UITableView *tv = (UITableView *) self.superview.superview;
BuyListController *vc = (BuyListController *) tv.dataSource;

1

代替超级视图,尝试使用[“ UItableViewvariable” visibleCells]。

我在foreach循环中使用它来遍历该应用程序看到且起作用的单元格。

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

奇迹般有效。


1

经过最少的测试,但是这个非通用的Swift 3示例似乎可以正常工作:

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

0

此代码`UITableView *tblView=[cell superview];将为您提供UItableview的实例,其中包含Tabe视图单元格


这将不起作用,因为UITableViewCell的直接父视图不是 UITableView。从iOS 7开始,UITableViewCell超级视图是UITableViewWrapperView。有关较不脆弱,更可靠的方法,请参见此处的其他答案。
JaredH 2014年

0

我建议您以这种方式遍历视图层次结构以找到父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 );
}

否则,当Apple 再次更改视图层次结构时,您的代码将中断。

另一个遍历层次结构的答案是递归的。


0
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 谢谢



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

0

来自@idris的答案我在Swift中为UITableViewCell写了一个扩展

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
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.