I am currently facing troubles using UITableViewDiffableDataSource
.
I would like to give a shot to this new feature, so I went on many tutorials on the net, but none of them seems to answer my issue.
In my current viewController I have a UITableView
, with 3 different objects (with different types each), but the UITableViewDiffableDataSource
is strongly typed to one.
Like: dataSource = UITableViewDiffableDataSource <SectionType, ItemType>
All my sections are fed with something like
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return bigObject.ObjectsOfType1.count
} else if section == 1 {
return bigObject.ObjectsOfType2.count
} else {
return bigObject.ObjectsOfType3.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! CustomTableViewCell
if indexPath.section == 0 {
cell.buildWithFirstObject(obj: bigObject.ObjectsOfType1[indexPath.row])
} else if indexPath.section == 1 {
cell.buildWithFirstObject(obj: bigObject.ObjectsOfType2[indexPath.row])
} else {
cell.buildWithFirstObject(obj: bigObject.ObjecstOfType3[indexPath.row])
}
}
Is there a trick to use diffable dataSource in my case ?
Any help is appreciated ! Thank you for reading me :)
Another possibility, that would prevent casting NSObject
to whatever you are expecting (which could be crash-prone), is to wrap your different objects as associated values in an enum
that conforms to Hashable
. Then, in your dequeue callback, you get the enum, and can unwrap the associated value. So something like
enum Wrapper: Hashable {
case one(Type1)
case two(Type2)
case three(Type3)
}
dataSource = UITableViewDiffableDataSource <SectionType, Wrapper>(collectionView: collectionView!) { [weak self] (collectionView: UICollectionView, indexPath: IndexPath, wrapper: Wrapper) -> UICollectionViewCell? in
switch wrapper {
case .one(let object):
guard let cell = dequeueReusableCell( ... ) as? YourCellType else { fatalError() }
// configure the cell
cell.prop1 = object.prop1
return cell
case .two(let object2):
guard let cell = dequeueReusableCell( ... ) as? YourCellType2 else { fatalError() }
// configure the cell
cell.prop1 = object2.prop1
return cell
case .three(let object3):
guard let cell = dequeueReusableCell( ... ) as? YourCellType3 else { fatalError() }
// configure the cell
cell.prop1 = object3.prop1
return cell
}
}
You could probably even simplify that with a single return
.
For the simplest approach, use AnyHashable
for the item identifier and an enum
for the section identifier. The problem with the accepted answer is that it adds an onerous amount of complexity as you add layers of functionality to the table because you must always start and finish with a generic enum
and not your custom type and before you know it there are switches everywhere. This kind of code doesn't read we at all. And I really hated seeing my datasources go from arrays of custom types to arrays of the same generic enum
.
And, contrary to what others have said, you cannot use Hashable
for the generic type because you can't use the protocol itself to conform to itself, which is why AnyHashable
exists, a "type-erased" substitute.
private enum Section {
case title
case kiwi
case mango
}
private struct Title: Hashable {}
private struct Kiwi: Hashable {
let identifier = UUID().uuidString
func hash(into hasher: inout Hasher) {
hasher.combine(identifier)
}
static func == (lhs: Kiwi, rhs: Kiwi) -> Bool {
return lhs.identifier == rhs.identifier
}
}
private struct Mango: Hashable {
let identifier = UUID().uuidString
func hash(into hasher: inout Hasher) {
hasher.combine(identifier)
}
static func == (lhs: Mango, rhs: Mango) -> Bool {
return lhs.identifier == rhs.identifier
}
}
var dataSource: UITableViewDiffableDataSource<Section, AnyHashable>!
dataSource = UITableViewDiffableDataSource(tableView: tableView,
cellProvider: { (tableView, indexPath, item) -> UITableViewCell? in
switch item {
case is Title:
return TitleCell()
case let item as Kiwi:
let cell = tableView.dequeueReusableCell(withIdentifier: someIdentifier,
for: indexPath) as? SomeCell
cell?.label.text = item.identifier
return cell
case let item as Mango:
let cell = tableView.dequeueReusableCell(withIdentifier: someIdentifier,
for: indexPath) as? AnotherCell
cell?.label.text = item.identifier
return cell
default:
return nil
}
})
var initialSnapshot = NSDiffableDataSourceSnapshot<Section, AnyHashable>()
initialSnapshot.appendSections([.title, .kiwi, .mango])
initialSnapshot.appendItems([Title()], toSection: .title)
initialSnapshot.appendItems([k1, k2, k3], toSection: .kiwi)
initialSnapshot.appendItems([m1, m2, m3], toSection: .mango)
self.dataSource.apply(initialSnapshot, animatingDifferences: false)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With