何时使用dequeueReusableCellWithIdentifier与dequeueReusableCellWithIdentifier:forIndexPath


167

dequeueReusableCellWithIdentifier有两个重载,我想确定何时应该使用一个重载?

关于forIndexPath函数状态的苹果文档, “此方法使用索引路径基于表视图中单元格的位置执行其他配置。”

我不确定该如何解释?

Answers:


216

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

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

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

第200节-WWDC 2012的Cocoa Touch新增功能讨论了(当时的新功能)forIndexPath:大约8时30分开始版本。它说“您将始终获得一个初始化的单元格”(没有提到如果您没有注册一个类或笔尖,它将崩溃)。

该视频还说“这将是该索引路径的正确大小”。大概这意味着它将通过查看表视图自身的宽度并调用委托的tableView:heightForRowAtIndexPath:方法(如果已定义)来设置单元格的大小,然后再返回它。 这就是为什么它需要索引路径。


真的很有帮助,谢谢。在出队时调整单元大小似乎对自动调整大小和布局约束没有多大好处?
约翰

38

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

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

dequeueReusableCellWithIdentifier:当您想支持iOS 5及更低版本时,要回答您的问题,因为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的单元出队-必须注册一个笔尖或标识符的类或在情节
提要中

@binarystar您必须注册一个自定义单元的笔尖或类,以确保它已加载。像:[self.tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"];
GoodSp33d 2014年

6

我从未理解过苹果为什么要创建更新的方法dequeueReusableCellWithIdentifier:forIndexPath:。他们关于他们的文件还不完整,并且具有误导性。我能够辨别这两种方法之间的唯一区别是,如果较旧的方法找不到找到传递了标识符的单元格,则该较旧的方法可以返回nil,而如果较新的方法无法返回,则该方法将崩溃。一个细胞。如果正确设置了标识符,则保证这两种方法都将返回一个单元格,并将该单元格放在情节提要中。如果您注册了类或xib,并且将您的单元设置为代码或xib文件,则也保证这两种方法都将返回单元格。


3
新方法使用索引路径来确定单元格的正确大小。
rob mayoff 2014年

1
@robmayoff但这有什么意义吗?如果没有新方法,仍然可以正确设置单元格的大小。新方法可以提供任何便利吗?
fujianjin6471 2015年

1
阅读我的答案的最后一段以获取详细信息。
rob mayoff

那么这是否意味着,如果表中所有单元格的大小都相同,那么我调用哪种方法都没关系?
Happiehappie

2
如果我提供tableView.estimateHeight,则单元格的大小也将正确确定。我仍然没有从新方法中受益。
瑞安

1

简而言之:

dequeueReusableCell(withIdentifier, for)仅适用于原型单元。如果您在没有原型单元的情况下尝试使用它,则会使应用程序崩溃。

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


-2

如果您使用的是动态生成的内容,则建议同时使用两者。否则,您的应用程序可能会意外崩溃。您可以实现自己的功能以检索可选的可重用单元格。如果是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()
    }
}
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.