Есть две перегрузки для dequeueReusableCellWithIdentifier, и я пытаюсь определить, когда следует использовать один или другой?
В яблочных документах, касающихся функции forIndexPath, говорится: "Этот метод использует путь индекса для выполнения дополнительной настройки, основанной на позиции ячеек в представлении таблицы".
Я не уверен, как это интерпретировать?
Самое важное отличие состоит в том, что версия forIndexPath:
утверждает (сбой), если вы не зарегистрировали класс или ниб для идентификатора. Более старая версия (forIndexPath:
) возвращает nil
в этом случае.
Вы регистрируете класс для идентификатора, отправив registerClass:forCellReuseIdentifier:
в представление таблицы. Вы регистрируете наконечник для идентификатора, отправив registerNib:forCellReuseIdentifier:
в представление таблицы.
Если вы создаете представление в виде таблицы и прототипы соты в раскадровке, загрузчик раскадровки позаботится о регистрации прототипов соты, которые вы определили в раскадровке.
Сессия 200 - Что нового в Cocoa Touch от WWDC 2012 обсуждает версию (тогда-новое) forIndexPath:
, начиная с 8m30s. В нем говорится, что "вы всегда будете получать инициализированную ячейку" (без упоминания о том, что она выйдет из строя, если вы не зарегистрировали класс или ник).
В видео также говорится, что "это будет правильный размер для этого указательного пути". Предположительно это означает, что он установит размер ячейки перед ее возвратом, просмотрев собственную ширину таблицы и вызов метод делегата tableView:heightForRowAtIndexPath:
(если он определен). Вот почему ему нужен указательный путь.
dequeueReusableCellWithIdentifier:forIndexPath:
будет всегда возвращать ячейку. Он либо повторно использует существующие ячейки, либо создает новый, и возвращает, если нет ячеек.
В то время как традиционный dequeueReusableCellWithIdentifier:
вернет ячейку, если она существует. Если есть ячейка, которую можно повторно использовать, она возвращает это, иначе она возвращает nil. Таким образом, вам нужно будет написать условие для проверки значения nil
.
Чтобы ответить на ваш вопрос, используйте dequeueReusableCellWithIdentifier:
, если вы хотите поддерживать iOS 5 и более низкие версии, поскольку dequeueReusableCellWithIdentifier:forIndexPath
доступен только на iOS 6 +
[self.tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"];
Я никогда не понимал, почему Apple создала новый метод, dequeueReusableCellWithIdentifier: forIndexPath:. Их документация на них не является полной и несколько вводит в заблуждение. Единственное различие, которое я смог различить между этими двумя методами, заключается в том, что этот старший метод может возвращать нуль, если он не находит ячейку с переданным идентификатором, в то время как новый метод выходит из строя, если он не может вернуться клетка. Оба метода гарантированно возвращают ячейку, если вы правильно установили идентификатор и сделали ячейку в раскадровке. Оба метода также гарантируют возврат ячейки, если вы зарегистрируете класс или xib, и сделаете свою ячейку в коде или файле xib.
Короче:
dequeueReusableCell(withIdentifier, for)
работает только с прототипом клетки. Если вы попытаетесь использовать его, когда ячейка прототипа отсутствует, это приведет к сбою приложения.
Hollemans M. 2016, глава 2 Контрольный список, IOS Apprentice (5-е издание). pp. 156.
Я бы рекомендовал использовать оба варианта, если вы используете динамически сгенерированный контент. В противном случае приложение может неожиданно аварийно завершить работу. Вы можете реализовать свою собственную функцию для извлечения дополнительной ячейки многократного использования. Если это 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()
}
}