【问题标题】:When to use dequeueReusableCellWithIdentifier vs dequeueReusableCellWithIdentifier : forIndexPath何时使用 dequeueReusableCellWithIdentifier 与 dequeueReusableCellWithIdentifier :forIndexPath
【发布时间】:2014-11-07 16:49:21
【问题描述】:

dequeueReusableCellWithIdentifier 有两个重载,我正在尝试确定何时应该使用一个与另一个?

关于 forIndexPath 函数的苹果文档指出,“此方法使用索引路径根据单元格在表格视图中的位置执行附加配置。”

我不知道怎么解释?

【问题讨论】:

    标签: ios objective-c swift


    【解决方案1】:

    主要区别是你不能为同一个 indexPath 注册两个单元格,而只能使用你可以做到的重用标识符,如果单元格没有针对该表视图注册,则两者都可以返回 nil

    【讨论】:

      【解决方案2】:

      简称:

      dequeueReusableCell(withIdentifier, for) 仅适用于原型 细胞。如果您在原型单元不存在时尝试使用它,则会导致应用崩溃。

      Hollemans M. 2016 年,第 2 章清单,IOS 学徒(第 5 版)。第 156 页。

      【讨论】:

        【解决方案3】:

        dequeueReusableCellWithIdentifier:forIndexPath:总是返回一个单元格。它要么重新使用现有单元格,要么创建一个新单元格,如果没有单元格则返回。

        而传统的dequeueReusableCellWithIdentifier: 将返回一个单元格(如果存在),即如果有一个单元格可以重复使用,则返回,否则返回 nil。因此,您还必须编写一个条件来检查 nil 值。

        如果您想支持 iOS 5 及更低版本,请使用 dequeueReusableCellWithIdentifier: 回答您的问题,因为 dequeueReusableCellWithIdentifier:forIndexPath 仅适用于 iOS 6+

        参考:https://developer.apple.com/library/ios/documentation/uikit/reference/UITableView_Class/Reference/Reference.html#//apple_ref/occ/instm/UITableView/dequeueReusableCellWithIdentifier:forIndexPath:

        【讨论】:

        • 不,它不会总是返回单元格 2014-12-26 07:56:39.947 testProg[4024:42920390] *** -[UITableView] 中的断言失败dequeueReusableCellWithIdentifier:forIndexPath:], /SourceCache/UIKit_Sim/UIKit-3318.65/UITableView.m:6116 2014-12-26 07:56:39.954 Interphase[4024:42920390] *** 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因: '无法使具有标识符 MyCustomCellIdentifier 的单元格出列 - 必须为标识符注册一个 nib 或一个类或连接故事板中的原型单元格'
        • @binarystar 您必须注册您的自定义单元格的 nib 或类,以便加载视图。喜欢:[self.tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"];
        【解决方案4】:

        如果您使用的是动态生成的内容,我建议您同时使用两者。否则您的应用程序可能会意外崩溃。您可以实现自己的函数来检索可选的可重用单元格。如果是nil,你应该返回一个不可见的空单元格:

        斯威夫特 3

        // Extensions to UITableView
        extension UITableView
        {
            // returns nil, if identifier does not exist. 
            // Otherwise it returns a configured cell for the given index path
            open func tryDequeueReusableCell (
                withIdentifier identifier: String, 
                for indexPath: IndexPath) -> UITableViewCell?
            {
                let cell = self.dequeueReusableCell(withIdentifier: identifier)
                if cell != nil {
                    return self.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
                }  
                return nil
            }
        }
        

        以及返回空单元格的扩展:

        // Extension to UITableViewCell
        extension UITableViewCell
        {
            // Generates an empty table cell that is not visible
            class func empty() -> UITableViewCell
            {
                let emptyCell = UITableViewCell(frame:CGRect(x:0, y:0, width:0, height:0))
                emptyCell.backgroundColor = UIColor.clear
                return emptyCell
            }
        }
        

        如何使用它的完整示例:

        import Foundation
        import UIKit
        
        // A protocol is used to identify if we can configure
        // a cell with CellData
        protocol ConfigureAbleWithCellData
        {
            func configure(_ data: CellData)
        }
        
        class MyCustomTableViewCell :
            UITableViewCell,
            ConfigureAbleWithCellData
        {
            @IBOutlet weak var title:UILabel! = nil
            func configure(_ data: CellData)
            {
                self.title.text = data.title
            }
        }
        
        // This actually holds the data for one cell
        struct CellData
        {
            var title:String = ""
            var reusableId:String = ""
        }
        
        class CosmoConverterUnitTableViewController:
            UIViewController,
            UITableViewDelegate,
            UITableViewDataSource
        {
            // Storage
            var data = Array<Array<CellData>>()
        
            func loadData()
            {
                var section1:[CellData] = []
                var section2:[CellData] = []
        
                section1.append(CellData(title:"Foo", reusableId:"cellType1"))
                section2.append(CellData(title:"Bar", reusableId:"cellType2"))
        
                data.append(section1)
                data.append(section2)
            }
        
            func tableView(_ tableView: UITableView,
                           numberOfRowsInSection section: Int) -> Int
            {
                return data[section].count
            }
        
            public func numberOfSections(in tableView: UITableView) -> Int
            {
                return data.count
            }
        
            func tableView(
                _ tableView: UITableView,
                cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
                guard
                    indexPath.row < data[indexPath.section].count
                    else
                {
                    fatalError("this can't be")
                }
        
                let cellData = data[indexPath.section][indexPath.row]
        
                if let cell = tableView.tryDequeueReusableCell(
                    withIdentifier: cellData.reusableId,
                    for: indexPath)
                {
                    if let configurableCell = cell as? ConfigureAbleWithCellData
                    {
                        configurableCell.configure(cellData)
                    }
                    else
                    {
                        // cell is not of type ConfigureAbleWithCellData
                        // so we cant configure it.
                    }
                    return cell
                }
                // id does not exist
                return UITableViewCell.empty()
            }
        }
        

        【讨论】:

          【解决方案5】:

          最重要的区别是forIndexPath: 版本在您没有为标识符注册类或 nib 时断言(崩溃)。在这种情况下,较旧的(非forIndexPath:)版本返回nil

          您通过将registerClass:forCellReuseIdentifier: 发送到表视图来为标识符注册一个类。您可以通过将registerNib:forCellReuseIdentifier: 发送到表格视图来为标识符注册一个笔尖。

          如果您在情节提要中创建表格视图和单元格原型,情节提要加载器会负责注册您在情节提要中定义的单元格原型。

          Session 200 - What's New in Cocoa Touch from WWDC 2012 在 8 分 30 秒左右开始讨论(当时是新的)forIndexPath: 版本。它说“你总是会得到一个初始化的单元格”(没有提到如果你没有注册类或 nib 会崩溃)。

          该视频还说“它将是该索引路径的正确大小”。大概这意味着它将在返回之前设置单元格的大小,方法是查看表格视图自己的宽度并调用您的委托的tableView:heightForRowAtIndexPath: 方法(如果已定义)。 这就是它需要索引路径的原因。

          【讨论】:

          • 这真的很有帮助,谢谢。在出队时调整单元格大小似乎没有自动调整大小和布局约束的优势?
          【解决方案6】:

          我一直不明白为什么 Apple 创建了更新的方法 dequeueReusableCellWithIdentifier:forIndexPath:。他们关于它们的文档不完整,并且有些误导。我能够辨别这两种方法的唯一区别是,如果旧方法没有找到传入标识符的单元格,则旧方法可以返回 nil,而新方法如果无法返回则崩溃一个细胞。如果您正确设置了标识符,则这两种方法都保证返回一个单元格,并将单元格放在情节提要中。如果您注册了一个类或 xib,这两种方法也保证返回一个单元格,并在代码或 xib 文件中创建您的单元格。

          【讨论】:

          • 新方法使用索引路径来确定单元格的合适大小。
          • @robmayoff 但这有什么意义吗?如果没有新方法,仍然可以正确设置单元格的大小。新方法可以提供任何便利吗?
          • 阅读我回答的最后一段了解详情。
          • 那么这是否意味着,如果我所有的单元格在表格中的大小都相同,那么我调用哪种方法并不重要?
          • 如果我提供tableView.estimateHeight,单元格的大小也将被正确确定。我仍然没有从新方法中受益。
          猜你喜欢
          • 2013-08-19
          • 2014-12-01
          • 2011-02-25
          • 1970-01-01
          • 2016-06-29
          • 2023-03-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多