UITableViewDiffableDataSource с разными объектами

В настоящее время у меня проблемы с использованием UITableViewDiffableDataSource.

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

В моем текущем viewController у меня есть UITableView, с 3 разными объектами (с разными типами каждый), но UITableViewDiffableDataSource строго типизирован к одному.

Подобно: dataSource = UITableViewDiffableDataSource <SectionType, ItemType>

Все мои разделы питаются чем-то вроде

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])
    }
}

Есть ли уловка для использования diffable dataSource в моем случае?

Любая помощь приветствуется! Спасибо, что прочитали меня:)

3 ответа

Другая возможность, которая помешает кастингу NSObject к тому, чего вы ожидаете (что может быть подвержено сбоям), заключается в том, чтобы обернуть ваши различные объекты как связанные значения в enum что соответствует Hashable. Затем в обратном вызове dequeue вы получаете перечисление и можете развернуть связанное значение. Так что-то вроде

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
  }
}

Возможно, вы даже могли бы упростить это с помощью одного return.

Для простейшего подхода используйте для идентификатора элемента и для идентификатора раздела. Проблема с принятым ответом заключается в том, что он добавляет обременительную сложность при добавлении уровней функциональности в таблицу, потому что вы всегда должны начинать и заканчивать с помощью общего, а не вашего пользовательского типа, и, прежде чем вы это узнаете, везде есть переключатели. Такой код вообще не читает нас. И мне действительно не нравилось видеть, как мои источники данных переходят от массивов пользовательских типов к массивам одного и того же общего .

И, вопреки тому, что говорили другие, вы не можете использовать для универсального типа, потому что вы не можете использовать сам протокол, чтобы соответствовать самому себе, поэтому существует, замена "стиранного типа".

      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)

Кажется, что с помощью UITableViewDiffableDataSource<Section, NSObject>и наличие у меня другого объекта, унаследованного от NSObject, отлично работает.

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