RxDataSources icon indicating copy to clipboard operation
RxDataSources copied to clipboard

Refresh Bug on UICollectionReusableView

Open StefaniOSApps opened this issue 5 years ago • 0 comments

What do i want to do?

I use a UICollectionView and UICollectionReusableView for headers and footers. I would now like to remove animated the header in a section by an update.

Problem

The identity of the section remains unchanged so that the change is animated. Unfortunately, this means that the header + footer are not updated when I perform an update. If the identity of the section is changed with the update, the header and footer are updated - but not animated (only reload).

Workaround

viewModel.sections.subscribe(onNext: { _ in
   // requierd for header footer refresh animation
   self.collectionView.collectionViewLayout.invalidateLayout()
}).disposed(by: bag)

My Code

Init + Refresh Section

final class DashboardViewModel: BIACollectionViewModel {

  var sections: BehaviorRelay<[BIACollectionSection]>

  init() {
    self.sections = BehaviorRelay(value: [
      BIACollectionSection(
        identity: "1",
        header: HFSingleLineViewModel(text: "Do you need any help?")
        viewModels: [
          VehicleViewModel(identifier: 1)
        ])
    ])

    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
      self.sections.accept([
        BIACollectionSection(
          identity: "1",
          viewModels: [
            VehicleViewModel(identifier: 1)
          ])
      ])
    }
  }
}

AnimatableSectionModelType

struct BIACollectionSection: AnimatableSectionModelType {
  typealias Item = CollectionViewCellTypes

  var header: CollectionHeaderFooterViewRepresentable?
  var footer: CollectionHeaderFooterViewRepresentable?
  var items: [Item]
  private let customIdentity: String?
  var identity: String {
    if let identity = customIdentity {
      return identity
    }
    let hView = header?.identity ?? ""
    let fView = footer?.identity ?? ""
    return hView + "_" + fView
  }

  init(original: Self, items: [Item]) {
    self = original
    self.items = items
  }

  internal init(
    identity: String? = nil,
    header: CollectionHeaderFooterViewRepresentable? = nil,
    footer: CollectionHeaderFooterViewRepresentable? = nil,
    viewModels: [CollectionCellRepresentable]
  ) {
    self.customIdentity = identity
    self.header = header
    self.footer = footer
    self.items = viewModels.map({ $0.item })
  }
}

Init DataSource

self.dataSource = RxCollectionViewSectionedAnimatedDataSource<BIACollectionSection>(
  configureCell: { _, collectionView, indexPath, item in
    let item = item.cell(collectionView: collectionView, indexPath: indexPath)
    return item
  },
  configureSupplementaryView: { _, collectionView, kind, indexPath -> UICollectionReusableView in
    let section = viewModel.sections.value.section(at: indexPath.section)
    switch kind {
    case UICollectionView.elementKindSectionHeader:
      return section.header?.headerFooterInstance(collectionView, indexPath: indexPath) ?? UICollectionReusableView(frame: .zero)
    case UICollectionView.elementKindSectionFooter:
      return section.footer?.headerFooterInstance(collectionView, indexPath: indexPath) ?? UICollectionReusableView(frame: .zero)
    default:
      return UICollectionReusableView(frame: .zero)
    }
  }
)

StefaniOSApps avatar Sep 28 '20 09:09 StefaniOSApps