Как правильно зарегистрировать ячейку в UITableView
Что проверяют
Понимаете ли вы механизм переиспользования ячеек и откуда UITableView берёт ячейку, когда очередь пуста. Следом обычно спрашивают, чем отличаются два метода dequeueReusableCell и нужна ли регистрация для прототипа из сториборда.
Как отвечать
Регистрация говорит таблице, из чего создать ячейку, если в очереди переиспользования свободной нет. Вариант зависит от того, как сделана ячейка:
- ячейка в коде:
tableView.register(MyCell.self, forCellReuseIdentifier: MyCell.reuseIdentifier), таблица вызоветinit(style:reuseIdentifier:); - ячейка в xib:
register(UINib(nibName: "MyCell", bundle: nil), forCellReuseIdentifier: ...), имя nib и идентификатор в самом xib должны совпадать с кодом; - прототип в сториборде: регистрировать ничего не нужно, достаточно указать Identifier в инспекторе;
- хедеры и футеры:
register(_:forHeaderFooterViewReuseIdentifier:)и наследникUITableViewHeaderFooterView.
Регистрируют один раз в viewDidLoad, до первой загрузки данных. Ячейку получают через dequeueReusableCell(withIdentifier:for:): он всегда возвращает ячейку, а при незарегистрированном идентификаторе падает сразу, что проще отлаживать. Старый dequeueReusableCell(withIdentifier:) без indexPath возвращает опционал и новую ячейку не создаёт.
Хороший тон: идентификатор через static var reuseIdentifier: String { String(describing: self) } и дженерик-хелпер dequeue<T: UITableViewCell>(for:) с приведением типа внутри. Для стандартных ячеек Apple рекомендует UIListContentConfiguration через cell.contentConfiguration вместо textLabel.
Где спотыкаются
Регистрируют класс для прототипа, который уже есть в сториборде. Регистрация в коде перекрывает прототип, ячейка создаётся без его интерфейса, и все аутлеты оказываются nil.
Ошибаются в идентификаторе или имени nib и получают падение «unable to dequeue a cell with identifier». Поэтому идентификатор держат в одной константе.
Настраивают ячейку в init и забывают про переиспользование: старая картинка или состояние кнопки переезжают на другую строку. Сброс нужен в prepareForReuse() или полная конфигурация в каждом вызове cellForRowAt.