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

167

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

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

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

Джаджа Харрис
источник

Ответы:

216

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

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

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

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

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

Роб Майофф
источник
Это действительно полезно, спасибо. Размер ячейки во время ожидания кажется меньшим преимуществом с ограничениями автоматического размера и макета?
Бенджон
38

dequeueReusableCellWithIdentifier:forIndexPath:всегда будет возвращать ячейку. Он либо повторно использует существующие ячейки, либо создает новую и возвращает, если ячеек нет.

Хотя традиционный dequeueReusableCellWithIdentifier:возвратит ячейку, если она существует, т. Е. Если есть ячейка, которую можно использовать повторно, она возвращает, а в противном случае возвращает ноль. Таким образом, вы должны написать условие, чтобы проверить на 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 :

GoodSp33d
источник
Нет, он не всегда возвращает ячейку 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 - необходимо зарегистрировать ниб» или класс для идентификатора или подключите ячейку прототипа в раскадровке »
clearlight
@binarystar Вы должны зарегистрировать перо или класс вашей пользовательской ячейки, поскольку он загружен. как:[self.tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"];
GoodSp33d
6

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

rdelmar
источник
3
Новый метод использует путь индекса для определения правильного размера ячейки.
Роб Майофф
1
@robmayoff Но имеет ли это какой-то смысл? Без нового метода размер ячейки все еще можно установить правильно. Может ли новый метод предложить какое-либо удобство?
fujianjin6471
1
Прочитайте последний абзац моего ответа для деталей.
Роб Майофф
Значит ли это, что, если все мои ячейки имеют одинаковые размеры в таблице, не имеет значения, какой метод я вызываю?
Happiehappie
2
Если я предоставлю tableView.estimateHeight, размер ячейки также будет определен правильно. Я все еще не получаю выгоду от нового метода.
Райан
1

Коротко:

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

Холлеманс М. 2016, глава 2 Контрольный список, ученик IOS (5-е издание). С. 156.

SLN
источник
-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()
    }
}
hhamm
источник