Подтвердить что ты не робот

Когда использовать dequeueReusableCellWithIdentifier vs dequeueReusableCellWithIdentifier: forIndexPath

Есть две перегрузки для dequeueReusableCellWithIdentifier, и я пытаюсь определить, когда следует использовать один или другой?

В яблочных документах, касающихся функции forIndexPath, говорится: "Этот метод использует путь индекса для выполнения дополнительной настройки, основанной на позиции ячеек в представлении таблицы".

Я не уверен, как это интерпретировать?

4b9b3361

Ответ 1

Самое важное отличие состоит в том, что версия forIndexPath: утверждает (сбой), если вы не зарегистрировали класс или ниб для идентификатора. Более старая версия (forIndexPath:) возвращает nil в этом случае.

Вы регистрируете класс для идентификатора, отправив registerClass:forCellReuseIdentifier: в представление таблицы. Вы регистрируете наконечник для идентификатора, отправив registerNib:forCellReuseIdentifier: в представление таблицы.

Если вы создаете представление в виде таблицы и прототипы соты в раскадровке, загрузчик раскадровки позаботится о регистрации прототипов соты, которые вы определили в раскадровке.

Сессия 200 - Что нового в Cocoa Touch от WWDC 2012 обсуждает версию (тогда-новое) forIndexPath:, начиная с 8m30s. В нем говорится, что "вы всегда будете получать инициализированную ячейку" (без упоминания о том, что она выйдет из строя, если вы не зарегистрировали класс или ник).

В видео также говорится, что "это будет правильный размер для этого указательного пути". Предположительно это означает, что он установит размер ячейки перед ее возвратом, просмотрев собственную ширину таблицы и вызов метод делегата tableView:heightForRowAtIndexPath: (если он определен). Вот почему ему нужен указательный путь.

Ответ 2

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:

Ответ 3

Я никогда не понимал, почему Apple создала новый метод, dequeueReusableCellWithIdentifier: forIndexPath:. Их документация на них не является полной и несколько вводит в заблуждение. Единственное различие, которое я смог различить между этими двумя методами, заключается в том, что этот старший метод может возвращать нуль, если он не находит ячейку с переданным идентификатором, в то время как новый метод выходит из строя, если он не может вернуться клетка. Оба метода гарантированно возвращают ячейку, если вы правильно установили идентификатор и сделали ячейку в раскадровке. Оба метода также гарантируют возврат ячейки, если вы зарегистрируете класс или xib, и сделаете свою ячейку в коде или файле xib.

Ответ 4

Я бы рекомендовал использовать оба варианта, если вы используете динамически сгенерированный контент. В противном случае приложение может неожиданно аварийно завершить работу. Вы можете реализовать свою собственную функцию для извлечения дополнительной ячейки многократного использования. Если это 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()
    }
}

Ответ 5

Короче:

dequeueReusableCell(withIdentifier, for) работает только с прототипом клетки. Если вы попытаетесь использовать его, когда ячейка прототипа отсутствует, это приведет к сбою приложения.

Hollemans M. 2016, глава 2 Контрольный список, IOS Apprentice (5-е издание). pp. 156.