diff --git a/Sources/Views/ReloadableView.swift b/Sources/Views/ReloadableView.swift index 8842f93..4a5aa68 100644 --- a/Sources/Views/ReloadableView.swift +++ b/Sources/Views/ReloadableView.swift @@ -54,10 +54,10 @@ extension UICollectionView: ReloadableView { @objc open func reloadDataSynchronously() { - reloadData() - - // Force a layout so that it is safe to call insert after this. - layoutIfNeeded() + DispatchQueue.main.async { + self.reloadData() + self.layoutIfNeeded() // Ensures layout is updated after reload + } } @objc @@ -69,38 +69,43 @@ extension UICollectionView: ReloadableView { @objc open func perform(batchUpdates: BatchUpdates, completion: (() -> Void)?) { - performBatchUpdates({ - if batchUpdates.insertItems.count > 0 { - self.insertItems(at: batchUpdates.insertItems) - } - if batchUpdates.deleteItems.count > 0 { - self.deleteItems(at: batchUpdates.deleteItems) - } - if batchUpdates.reloadItems.count > 0 { - self.reloadItems(at: batchUpdates.reloadItems) - } - for move in batchUpdates.moveItems { - self.moveItem(at: move.from, to: move.to) - } - - if batchUpdates.insertSections.count > 0 { - self.insertSections(batchUpdates.insertSections) - } - if batchUpdates.deleteSections.count > 0 { - self.deleteSections(batchUpdates.deleteSections) - } - if batchUpdates.reloadSections.count > 0 { - self.reloadSections(batchUpdates.reloadSections) - } - for move in batchUpdates.moveSections { - self.moveSection(move.from, toSection: move.to) - } - }, completion: { _ in - completion?() - }) + DispatchQueue.main.async { + self.performBatchUpdates({ + if batchUpdates.insertItems.count > 0 { + self.insertItems(at: batchUpdates.insertItems) + } + if batchUpdates.deleteItems.count > 0 { + self.deleteItems(at: batchUpdates.deleteItems) + } + if batchUpdates.reloadItems.count > 0 { + self.reloadItems(at: batchUpdates.reloadItems) + } + for move in batchUpdates.moveItems { + self.moveItem(at: move.from, to: move.to) + } + + if batchUpdates.insertSections.count > 0 { + self.insertSections(batchUpdates.insertSections) + } + if batchUpdates.deleteSections.count > 0 { + self.deleteSections(batchUpdates.deleteSections) + } + if batchUpdates.reloadSections.count > 0 { + self.reloadSections(batchUpdates.reloadSections) + } + for move in batchUpdates.moveSections { + self.moveSection(move.from, toSection: move.to) + } + }, completion: { _ in + completion?() + }) + } } - + open func contentView(forIndexPath indexPath: IndexPath) -> UIView? { + DispatchQueue.main.async { + self.layoutIfNeeded() // Ensure the layout is up-to-date before querying + } return self.cellForItem(at: indexPath)?.contentView } } @@ -112,53 +117,61 @@ extension UITableView: ReloadableView { @objc open func reloadDataSynchronously() { - reloadData() + DispatchQueue.main.async { + self.reloadData() + self.layoutIfNeeded() // Ensures layout is updated after reload + } } - + @objc open func registerViews(withReuseIdentifier reuseIdentifier: String) { register(UITableViewCell.self, forCellReuseIdentifier: reuseIdentifier) register(UITableViewHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: reuseIdentifier) } - + @objc open func perform(batchUpdates: BatchUpdates, completion: (() -> Void)?) { - beginUpdates() - - // Update items. - if batchUpdates.insertItems.count > 0 { - insertRows(at: batchUpdates.insertItems, with: .automatic) - } - if batchUpdates.deleteItems.count > 0 { - deleteRows(at: batchUpdates.deleteItems, with: .automatic) - } - if batchUpdates.reloadItems.count > 0 { - reloadRows(at: batchUpdates.reloadItems, with: .automatic) - } - for move in batchUpdates.moveItems { - moveRow(at: move.from, to: move.to) - } + DispatchQueue.main.async { + self.beginUpdates() + + // Update items. + if batchUpdates.insertItems.count > 0 { + self.insertRows(at: batchUpdates.insertItems, with: .automatic) + } + if batchUpdates.deleteItems.count > 0 { + self.deleteRows(at: batchUpdates.deleteItems, with: .automatic) + } + if batchUpdates.reloadItems.count > 0 { + self.reloadRows(at: batchUpdates.reloadItems, with: .automatic) + } + for move in batchUpdates.moveItems { + self.moveRow(at: move.from, to: move.to) + } - // Update sections. - if batchUpdates.insertSections.count > 0 { - insertSections(batchUpdates.insertSections, with: .automatic) - } - if batchUpdates.deleteSections.count > 0 { - deleteSections(batchUpdates.deleteSections, with: .automatic) - } - if batchUpdates.reloadSections.count > 0 { - reloadSections(batchUpdates.reloadSections, with: .automatic) - } - for move in batchUpdates.moveSections { - moveSection(move.from, toSection: move.to) + // Update sections. + if batchUpdates.insertSections.count > 0 { + self.insertSections(batchUpdates.insertSections, with: .automatic) + } + if batchUpdates.deleteSections.count > 0 { + self.deleteSections(batchUpdates.deleteSections, with: .automatic) + } + if batchUpdates.reloadSections.count > 0 { + self.reloadSections(batchUpdates.reloadSections, with: .automatic) + } + for move in batchUpdates.moveSections { + self.moveSection(move.from, toSection: move.to) + } + + self.endUpdates() + + completion?() } - - endUpdates() - - completion?() } - + open func contentView(forIndexPath indexPath: IndexPath) -> UIView? { + DispatchQueue.main.async { + self.layoutIfNeeded() // Ensure the layout is up-to-date before querying + } return self.cellForRow(at: indexPath)?.contentView } } diff --git a/Sources/Views/ReloadableViewLayoutAdapter+UICollectionView.swift b/Sources/Views/ReloadableViewLayoutAdapter+UICollectionView.swift index a566acb..77c6d18 100644 --- a/Sources/Views/ReloadableViewLayoutAdapter+UICollectionView.swift +++ b/Sources/Views/ReloadableViewLayoutAdapter+UICollectionView.swift @@ -44,18 +44,28 @@ extension ReloadableViewLayoutAdapter: UICollectionViewDataSource { /// - Warning: Subclasses that override this method must call super open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - let arrangement = currentArrangement[safe: indexPath.section]?.items[safe: indexPath.item] + + // Dequeue cell with unique reuse identifier let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) - UIView.performWithoutAnimation { - arrangement?.makeViews(in: cell.contentView) + + // Reset cell state + cell.isHidden = false + cell.contentView.subviews.forEach { $0.removeFromSuperview() } + + // Configure cell content + if let arrangement = currentArrangement[safe: indexPath.section]?.items[safe: indexPath.item] { + UIView.performWithoutAnimation { + arrangement.makeViews(in: cell.contentView) + } } + return cell } /// - Warning: Subclasses that override this method must call super open func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: reuseIdentifier, for: indexPath) - let arrangement: LayoutArrangement? + var arrangement: LayoutArrangement? switch kind { case UICollectionView.elementKindSectionHeader: arrangement = currentArrangement[indexPath.section].header @@ -65,6 +75,16 @@ extension ReloadableViewLayoutAdapter: UICollectionViewDataSource { arrangement = nil assertionFailure("unknown supplementary view kind \(kind)") } + + // Reset supplementary view state + view.subviews.forEach { $0.removeFromSuperview() } + + // Configure supplementary view content + if kind == UICollectionView.elementKindSectionHeader { + arrangement = currentArrangement[indexPath.section].header + } else { + arrangement = currentArrangement[indexPath.section].footer + } UIView.performWithoutAnimation { arrangement?.makeViews(in: view) }