Skip to content

Commit

Permalink
ensure reload on main and reset cell state
Browse files Browse the repository at this point in the history
  • Loading branch information
martenolsson83 committed Jan 13, 2025
1 parent c9eda41 commit bd9dd3e
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 73 deletions.
151 changes: 82 additions & 69 deletions Sources/Views/ReloadableView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
}
Expand All @@ -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
}
}
28 changes: 24 additions & 4 deletions Sources/Views/ReloadableViewLayoutAdapter+UICollectionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
Expand Down

0 comments on commit bd9dd3e

Please sign in to comment.