【问题标题】:How to get UITableView from UITableViewCell?如何从 UITableViewCell 获取 UITableView?
【发布时间】:2013-03-20 15:36:37
【问题描述】:

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

【问题讨论】:

  • 这样做的目的是什么?
  • [cell superView] 也许?
  • 值得解释一下为什么你认为你需要这个 - 因为这可能是糟糕设计的一个标志,因为我真的想不出许多合理的理由让一个单元格知道它是否在屏幕上。
  • @Paul.s 我们在单元格中的图像上有一个手势识别器,当单元格被触摸时,它会打开另一个覆盖视图,想想弹出框样式,它应该根据需要覆盖尽可能多的单元格正确显示。为此,它需要提供给它的 TableView 或其他视图才能显示。对解决方案并不满意,但要获得所需的效果,获得 UITableViewCell 的 UITableView 是我们想出的最好的方法。
  • @chadbag 不用担心,希望我能给遇到同样问题的其他人一个想法。

标签: iphone ios objective-c xcode tableview


【解决方案1】:

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

Objective-C

id view = [cellInstance superview];

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

UITableView *tableView = (UITableView *)view;

斯威夫特

var view = cellInstance.superview
while (view != nil && (view as? UITableView) == nil) {
  view = view?.superview
}
        
if let tableView = view as? UITableView {
   tableView.beginUpdates()
   tableView.endUpdates()
}

【讨论】:

  • 谢谢。这似乎在 iOS 8 中再次发生了变化,并且很好地处理了所有版本。
  • 我建议在单元格中添加对 tableview 的弱引用,以避免将来更新时出现兼容性问题。
  • 比较弱的方式。如果将来视图的层次结构发生变化,它将无法工作。
  • 最好简单地创建一个实际上持有指向 tableview 的指针的弱属性。在子类@property (weak, nonatomic) UITableView *tableView;tableView:cellForRowAtIndexPath: 中只需设置cell.tableView = tableView;
  • 当前的经验是,在使单元格出列后,表格视图不会立即显示为单元格的超级视图 - 如果我将单元格滚动出视图并再次返回,我确实找到了一个表格视图超级视图(如其他答案中所述的递归搜索)。自动布局和可能的其他因素可能是原因。
【解决方案2】:

在 iOS7 beta 5 中,UITableViewWrapperViewUITableViewCell 的父视图。 UITableView 也是 UITableViewWrapperView 的超级视图。

所以对于 iOS 7 来说解决方案是

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

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

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

【讨论】:

  • 天哪,这是一个残酷的 API 更改。如果您同时支持 6 和 7,Apple 的最佳分支做法是什么?
  • @RyanRomanchuk 这是一个很好的建议:devforums.apple.com/message/865550#865550——在创建单元格时创建一个指向相关 tableView 的弱指针。或者,使用新方法 relatedTableView 创建一个 UITableViewCell 类别,该方法检查 iOS 版本并返回适当的 superView。
  • 为此在 UIView 上写一个递归类别。您无需检查版本;只需调用 superview 直到找到表格视图单元格或视图堆栈的顶部。这是我在 iOS 6 中使用的,它在 iOS 7 中无需修改即可工作。在 iOS 8 中应该可以正常工作。
  • 请见谅;我的意思是直到你找到表格视图。 :)
【解决方案3】:

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

【讨论】:

  • 经验法则是不要使用强制展开
  • @thibautnoah 在展开之前有一个view != nil 检查。更新为更简洁的代码
【解决方案4】:

在 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:添加对 UITableViewCell 的弱 UITableView 引用

@interface SOUITableViewCell

   @property (weak, nonatomic) UITableView *tableView;

@end

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

【讨论】:

  • 我不同意解决方案 2 是更好的设计。它有更多的故障点,需要有纪律的人工干预。第一个解决方案实际上非常可靠,尽管我会按照@idris 在他的回答中建议的那样实现它,以进行更多的未来验证。
  • 测试[self.superview.superview isKindOfClass:[UITableView class]]应该是第一个,因为iOS 7越来越多了。
【解决方案5】:

如果它是可见的,那么它就有一个超级视图。而且......惊喜......超级视图是一个 UITableView 对象。

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

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

iOS7 更新: Apple 在此处更改了子视图层次结构。像往常一样,在处理没有详细记录的事情时,总是存在事情发生变化的风险。在最终找到 UITableView 对象之前,“爬上”视图层次结构要省事得多。

【讨论】:

  • 这在 iOS7 中不再适用,在 iOS7 beta5 中 UITableViewWrapperView 是 UITableViewCell 的超级视图......现在导致我出现问题
  • 感谢您的评论。那我得检查一下我的一些代码。
【解决方案6】:

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

或更紧凑(感谢 kabiroberai):

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

请注意 UITableViewCell 仅在 cellForRowAtIndexPath 执行后才添加到 UITableView 上。

【讨论】:

  • 您可以将整个lookForSuperviewOfType: 方法体压缩成一行,使其更加快捷:return superview as? T ?? superview?.superviewOfType(type)
  • @kabiroberai,谢谢。我在答案中添加了您的建议。
  • 递归调用使用的名称需要匹配。所以这需要阅读:... ?? superview?.lookForSuperviewOfType(type)
【解决方案7】:

无论您最终通过调用超级视图还是通过响应者链来设法完成的工作都将非常脆弱。 做到这一点的最好方法,如果细胞想知道一些事情,就是将一个对象传递给细胞,该对象响应某种方法来回答细胞想问的问题,并让控制器实现确定要回答什么的逻辑(从你的问题我猜细胞想知道某些东西是否可见)。

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

表格视图单元格应该是只显示信息的虚拟视图。

【讨论】:

  • 通过代表也许会更好。由于父母给了我问题,我暂时使用了对表格的传递引用。
  • 我描述的基本上是使用委托模式:)
  • 这应该是公认的答案,人们看到了这个问题,看到了 150 多个赞成的答案,他们认为从单元格中获取 tableView 很好,不幸的是,更可能保留对它的强烈引用。
【解决方案8】:

我在 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

最好的,

【讨论】:

    【解决方案9】:

    这是基于上述答案的 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
        }
    }
    

    希望有帮助:)

    【讨论】:

      【解决方案10】:

      我基于 Gabe 的建议,即 UITableViewWrapperView 对象是 iOS7 beta5 中 UITableViewCell 对象的父视图。

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

      【讨论】:

        【解决方案11】:

        我从上面的答案中借用和修改了一点,并提出了以下sn-p。

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

        传入类提供了在其他一些场合遍历和获取除 UITableView 之外的视图的灵活性。

        【讨论】:

          【解决方案12】:

          我对这个问题的解决方案与其他解决方案有些相似,但使用了优雅的 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 &amp;&amp; ![view isKindOfClass:UITableView.class]; view = view.superview);
          【解决方案13】:
          UITableView *tv = (UITableView *) self.superview.superview;
          BuyListController *vc = (BuyListController *) tv.dataSource;
          

          【讨论】:

            【解决方案14】:

            尝试使用 ["UItableViewvariable" visibleCells],而不是 superview。

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

            像魅力一样工作。

            【讨论】:

              【解决方案15】:

              经过最低限度的测试,但这个非通用 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
                  }
              }
              

              【讨论】:

                【解决方案16】:

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

                【讨论】:

                • 这不起作用,因为 UITableViewCell 的直接父视图不是 UITableView。从 iOS 7 开始,UITableViewCell 父视图是 UITableViewWrapperView。有关不那么脆弱、更可靠的方法,请参阅此处的其他答案。
                【解决方案17】:

                我建议你这样遍历视图层次结构找到父 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 再次更改视图层次结构时,您的代码将中断。

                Another answer 也遍历层次结构是递归的。

                【讨论】:

                  【解决方案18】:
                  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 谢谢你

                  【讨论】:

                    【解决方案19】:

                    一行代码就可以搞定。

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

                    【讨论】:

                      【解决方案20】:
                      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
                          }
                      }
                      

                      【讨论】:

                        【解决方案21】:

                        来自@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
                        }
                        

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2014-04-14
                          • 2013-03-20
                          • 1970-01-01
                          相关资源
                          最近更新 更多