何时使用 dequeue eReusableCellWithIdentifier vs dequeue eReusableCellWithIdentifier: forIndexPath

Dequeue eReusableCellWithIdentifier 有两个重载,我试图确定什么时候应该使用其中一个而不是另一个?

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

我不知道该怎么解释?

60701 次浏览

我一直不明白苹果为什么要创建更新的方法 dequeue eReusableCellWithIdentifier: forIndexPath: 。他们的文档并不完整,而且有些误导。我能够辨别出这两个方法之间的唯一区别是,如果旧方法没有找到传入标识符的单元格,而新方法崩溃了,如果它不能返回单元格,那么旧方法可以返回 null。如果您正确地设置了标识符,那么这两种方法都保证返回一个单元格,并使该单元格成为故事板中的一个单元格。如果您注册了一个类或 xib,并使您的单元格成为代码或 xib 文件,那么这两种方法都保证返回一个单元格。

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

而传统的 dequeueReusableCellWithIdentifier:将返回一个单元格,如果它存在的话,也就是说,如果有一个单元格可以重用,它将返回另一个单元格,否则它将返回 null。因此,您还必须编写一个条件来检查 nil值。

为了回答你的问题,当你想要支持 iOS5或更低版本时,使用 dequeueReusableCellWithIdentifier:,因为 dequeueReusableCellWithIdentifier:forIndexPath只能在 iOS6 + 上使用

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

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

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

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

会议200-来自 WWDC 2012的可可触摸的最新消息 讨论了(当时新的) forIndexPath:版本在8m30s 左右开始。它说“您总是会得到一个初始化的单元格”(但是没有提到如果您没有注册一个类或者 nib,它就会崩溃)。

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

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

简而言之:

dequeueReusableCell(withIdentifier, for)只适用于原型机 如果你试图使用它时,原型细胞是不存在的,它会崩溃的应用程序。

霍尔曼斯 M 2016,第2章清单,IOS 学徒(第5版) . pp: 156。

主要区别在于,您不能为同一个 indexPath 注册两个单元格,而只能使用可以执行此操作的重用标识符,如果没有针对该表视图注册单元格,则两者都可以返回 null