【问题标题】:UICollectionViewDiffableDataSource is replacing data instead of updatingUICollectionViewDiffableDataSource 正在替换数据而不是更新
【发布时间】:2020-11-19 13:25:06
【问题描述】:

我正在努力理解UICollectionViewDiffableDataSourceNSDiffableDataSourceSnapshot

我在下面创建了一个非常粗略的版本。基本上在加载时它应该获取照片。

点击导航栏中的按钮即可获取下一页。然而,这只是替换现有数据,我希望它将值附加到数组中。

请问我应该如何更新我的数据而不是替换它?

class ViewController: UIViewController {

  lazy var collectionView = UICollectionView(frame: view.frame, collectionViewLayout: UICollectionViewFlowLayout())

  enum Section {
    case main
  }

  typealias DataSource = UICollectionViewDiffableDataSource<Section, AnyHashable>
  typealias DataSourceSnapshot = NSDiffableDataSourceSnapshot<Section, AnyHashable>

  private var dataSource: DataSource!
  private var snapshot = DataSourceSnapshot()

  override func viewDidLoad() {
    super.viewDidLoad()

    let updateButton = UIBarButtonItem(title: "Add", style: .plain, target: self, action: #selector(onTapLoad))
    navigationItem.rightBarButtonItem = updateButton

    view.addSubview(collectionView)
    collectionView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
    collectionView.delegate = self
    collectionView.backgroundColor = .systemBackground
    collectionView.register(PhotoCell.self, forCellWithReuseIdentifier: "PhotoCell")

    dataSource = DataSource(collectionView: collectionView, cellProvider: { (cv, indexPath, object) -> PhotoCell? in

      if let object = object as? Photo {
        let cell = cv.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as! PhotoCell
        cell.backgroundColor = indexPath.item % 2 == 0 ? .systemTeal : .systemPink
        cell.label.text = object.title
        return cell
      }

      return nil
    })

    load()
  }

  @objc func onTapLoad() {
    load(page: 1)
  }

  func load(page: Int = 0) {
    PhotoLoader.shared.load(page: page) { result in
      if let photos = try? result.get() {
        self.apply(photos)
      }
    }
  }

  func apply(_ photos: [Photo]) {

    snapshot = DataSourceSnapshot()
    snapshot.appendSections([Section.main])
    snapshot.appendItems(photos)

    dataSource.apply(snapshot, animatingDifferences: false)
  }
}

extension ViewController: UICollectionViewDelegateFlowLayout {
  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return .init(width: collectionView.frame.width - 32, height: 100)
  }
}

struct Photo: Decodable, Hashable {
  let id = UUID()
  let title: String
}

final class PhotoLoader {

  static let shared = PhotoLoader()

  func load(page: Int, completion: @escaping (Result<[Photo], Error>) -> Void) {

    URLSession.shared.dataTask(with: URL(string: "https://jsonplaceholder.typicode.com/photos?_start=\(page)&_limit=5")!, completionHandler: { data, response, error in
      if let data = data, let model = try? JSONDecoder().decode([Photo].self, from: data) {
        completion(.success(model))
      }
    }).resume()
  }
}

final class PhotoCell: UICollectionViewCell {
  lazy var label = UILabel(frame: .zero)

  override init(frame: CGRect) {
    super.init(frame: frame)

    label.translatesAutoresizingMaskIntoConstraints = false
    label.numberOfLines = 3
    addSubview(label)
    NSLayoutConstraint.activate([
      label.topAnchor.constraint(equalTo: topAnchor),
      label.leadingAnchor.constraint(equalTo: leadingAnchor),
      label.bottomAnchor.constraint(equalTo: bottomAnchor),
      label.trailingAnchor.constraint(equalTo: trailingAnchor)
    ])

  }

  required init?(coder: NSCoder) {
    return nil
  }

}

【问题讨论】:

    标签: ios swift uicollectionview nsdiffabledatasourcesnapshot uicollectionviewdiffabledatasource


    【解决方案1】:

    不使用额外的数组初始化viewDidLoad中的快照

    override func viewDidLoad() {
        super.viewDidLoad()
        
        ...
        let snapshot = DataSourceSnapshot()
        snapshot.appendSections([Section.main])
        dataSource.apply(snapshot, animatingDifferences: false)
    }
    

    apply(_ photos) 中将照片附加到当前快照而不是创建新快照

    func apply(_ photos: [Photo]) {
    
        var snapshot = dataSource.snapshot()
        snapshot.appendItems(photos, toSection: .main)  
        dataSource.apply(snapshot, animatingDifferences: true)
    }
    

    该属性不需要

    私有 var 快照 = DataSourceSnapshot() 罢工>

    注意:

    尽可能具体地声明数据源

    typealias DataSource = UICollectionViewDiffableDataSource<Section, Photo>
    typealias DataSourceSnapshot = NSDiffableDataSourceSnapshot<Section, Photo>
    

    然后你就摆脱了不必要的类型检查

    dataSource = DataSource(collectionView: collectionView, cellProvider: { (cv, indexPath, photo) -> PhotoCell? in
    
        let cell = cv.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as! PhotoCell
        cell.backgroundColor = indexPath.item % 2 == 0 ? .systemTeal : .systemPink
        cell.label.text = photo.title
        return cell
     
    })
    

    【讨论】:

      【解决方案2】:

      您每次都在为您的apply(_ photos: [Photo]) 方法提供一个新数据集。您需要为其提供更新的数据集,以便差异逻辑可以应用更改。

      现在你只是每次都给它新的数据。

      在数组中捕获您的 Photos 并使用 didSet 观察者调用 apply

        private var photos: [Photo] = [] {
          didSet { apply(photos) }
        }
      .....
        func load(page: Int = 0) {
          PhotoLoader.shared.load(page: page) { result in
            if let photos = try? result.get() {
              self.photos.append(contentsOf: photos)
            }
          }
        }
      
      

      【讨论】:

        猜你喜欢
        • 2018-07-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多