Зачем возвращать ноль tableViewCell для параметра cellProvider UITableViewDiffableDataSource?

iOS 13 имеет новый API для обработки tableView и одна интересная область API является cell параметр провайдера UITableViewDiffableDataSource

    public typealias CellProvider = (UITableView, IndexPath, ItemIdentifierType) -> UITableViewCell?

Когда было бы целесообразно вернуть nilUITableViewCell Вот?

1 ответ

Таким образом, этот API все еще находится в стадии бета-тестирования, что означает, что документация не завершена.

Здесь утверждается:

Эта документация содержит предварительную информацию о разрабатываемом API или технологии. Эта информация может быть изменена, и программное обеспечение, реализованное в соответствии с этой документацией, должно быть протестировано с окончательной версией программного обеспечения операционной системы.

TL; DR - на данный момент, если вы создаете UITableView и использовать UITableViewDiffableDataSource который возвращает nil, ваше приложение выйдет из строя.

Однако в этом сообщении блога рассматриваются некоторые новые детали. Однако в нем ничего не говорится о возврате nil для ячейки.

Вы также можете взглянуть на этот сеанс WWDC. Примерно через 15 минут вы можете увидеть, что пример кода выдает фатальную ошибку, если ячейка не может быть создана.

Используя приведенный выше блог, я сделал простой tableView в Xcode 11, подобный этому

class ViewController: UIViewController {

    enum Section: CaseIterable {
        case friends
        case family
        case coworkers
    }

    struct Contact: Hashable {
        var name: String
        var email: String
    }

    struct ContactList {
        var friends: [Contact]
        var family: [Contact]
        var coworkers: [Contact]
    }

    private let tableView = UITableView()
    private let cellReuseIdentifier = "cell"
    private lazy var dataSource = makeDataSource()

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(UITableViewCell.self,
                           forCellReuseIdentifier: cellReuseIdentifier
        )

        tableView.dataSource = dataSource

        view.addSubview(tableView)

        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

        loadData()
    }


    func makeDataSource() -> UITableViewDiffableDataSource<Section, Contact> {
        let reuseIdentifier = cellReuseIdentifier

        return UITableViewDiffableDataSource(
            tableView: tableView,
            cellProvider: {  tableView, indexPath, contact in
                let cell = tableView.dequeueReusableCell(
                    withIdentifier: reuseIdentifier,
                    for: indexPath
                )

                cell.textLabel?.text = contact.name
                cell.detailTextLabel?.text = contact.email
                return cell
            }
        )
    }

    func update(with list: ContactList, animate: Bool = true) {
        let snapshot = NSDiffableDataSourceSnapshot<Section, Contact>()
        snapshot.appendSections(Section.allCases)

        snapshot.appendItems(list.friends, toSection: .friends)
        snapshot.appendItems(list.family, toSection: .family)
        snapshot.appendItems(list.coworkers, toSection: .coworkers)

        dataSource.apply(snapshot, animatingDifferences: animate)
    }

    func loadData() {
        let friends = [
            Contact(name: "Bob", email: "Bob@gmail.com"),
            Contact(name: "Tom", email: "Tom@myspace.com")
        ]

        let family = [
            Contact(name: "Mom", email: "mom@aol.com"),
            Contact(name: "Dad", email: "dad@aol.com")
        ]

        let coworkers = [
            Contact(name: "Mason", email: "tim@something.com"),
            Contact(name: "Tim", email: "mason@something.com")
        ]

        let contactList = ContactList(friends: friends, family: family, coworkers: coworkers)
        update(with: contactList, animate: true)
    }
}

Все загружается нормально, поэтому я решил посмотреть, что произойдет, если я верну nil для ячейки, поэтому я добавил этот код в UITableViewDiffableDataSource:

if contact.name == "Bob" {
    return nil
}

Это привело к сбою:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource returned a nil cell for row at index path: <NSIndexPath: 0xd6d99b18b93a5a0e> {length = 2, path = 0 - 0}. Table view: <UITableView: 0x7f8d30006200; frame = (-207 -448; 414 896); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x60000239de00>; layer = <CALayer: 0x600002dd0ec0>; contentOffset: {0, 0}; contentSize: {414, 264}; adjustedContentInset: {0, 0, 0, 0}; dataSource: <_TtGC5UIKit29UITableViewDiffableDataSourceOC5iOS1314ViewController7SectionVS2_7Contact_: 0x600002ffc520>>, dataSource: <_TtGC5UIKit29UITableViewDiffableDataSourceOC5iOS1314ViewController7SectionVS2_7Contact_: 0x600002ffc520>' 

Фактически, просто возврат nil (без ячейки) также вызывает сбой, как только источник данных применяет обновление. Итак, на данный момент, насколько я могу судить, возвращение nil не является реальным вариантом, поскольку вызывает сбой.

Вы можете проверить полный проект на github.

Другие вопросы по тегам