何时使用dequeueReusableCellWithIdentifier vs dequeueReusableCellWithIdentifier:forIndexPath

时间:2014-09-13 18:36:03

标签: ios objective-c swift

dequeueReusableCellWithIdentifier有两个重载,我试图确定何时使用一个与另一个相比?

关于forIndexPath函数的apple文档声明,"此方法使用索引路径根据表格视图中单元格的位置执行其他配置。"

我不确定如何解释这个?

5 个答案:

答案 0 :(得分:197)

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

您可以通过将registerClass:forCellReuseIdentifier:发送到表格视图来注册标识符的类。通过将registerNib:forCellReuseIdentifier:发送到表视图来注册标识符的nib。

如果您在故事板中创建表格视图和单元格原型,则storyboard加载程序负责注册您在故事板中定义的单元格原型。

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

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

答案 1 :(得分:35)

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

答案 2 :(得分:6)

我从未理解为什么Apple创建了更新的方法,dequeueReusableCellWithIdentifier:forIndexPath:。他们关于它们的文件不完整,有些误导。我能够在两种方法之间区分的唯一区别是,如果找不到传入标识符的单元格,那么旧方法可以返回nil,而新方法崩溃,如果它不能返回一个细胞。如果已正确设置标识符,则保证两种方法都返回单元格,并在故事板中创建单元格。如果您注册类或xib,并且在代码或xib文件中创建单元格,这两种方法也可以保证返回单元格。

答案 3 :(得分:1)

简而言之:

  

dequeueReusableCell(withIdentifier, for)仅适用于原型   细胞。如果您在原型单元格缺失时尝试使用它,则会使应用程序崩溃。

Hollemans M。 2016,第2章清单, IOS Apprentice (第5版)。 pp:156。

答案 4 :(得分:-2)

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

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