为什么为 UITableViewDiffableDataSource cellProvider 参数返回 nil tab



iOS 13有一些新的API来处理tableView,API的一个有趣区域是UITableViewDiffableDataSourcecell提供程序参数

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

什么时候在这里返回nilUITableViewCell合适?

所以这个 API 仍处于测试阶段,这意味着文档不完整。

它指出:

本文档包含有关正在开发的 API 或技术的初步信息。此信息可能会更改,根据本文档实现的软件应使用最终操作系统软件进行测试。

TLDR - 截至目前,如果您创建一个UITableView并使用返回 nil 的UITableViewDiffableDataSource,您的应用程序将崩溃。

但是,这篇博文介绍了一些新的细节。不过,它没有提到任何关于为单元格返回 nil 的内容。

您还可以查看此WWDC会议。在 15 分钟左右,您可以看到如果无法创建单元格,示例代码会引发致命错误。

使用上面的博客,我在 Xcode 11 中制作了一个简单的 tableView,如下所示

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上查看完整的项目。

最新更新