Skip to content
This repository was archived by the owner on Nov 4, 2022. It is now read-only.

Commit 44e46a9

Browse files
V1.6.1 (#140)
* Further refinement of cell lifecycle * Workaround UICollectionViewCell bug * Fix for animating layoutChange when `shouldInvalidateLayoutOnStateChange` is enabled
1 parent 88a0ecf commit 44e46a9

File tree

10 files changed

+77
-107
lines changed

10 files changed

+77
-107
lines changed

Sources/ASCollectionView/Cells/ASCollectionViewCell.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,15 @@ class ASCollectionViewCell: UICollectionViewCell, ASDataSourceConfigurableCell
6060
}
6161
}
6262

63+
var shouldSkipNextRefresh: Bool = true
64+
6365
override func prepareForReuse()
6466
{
6567
indexPath = nil
6668
itemID = nil
6769
isSelected = false
6870
hostingController = nil
71+
shouldSkipNextRefresh = true
6972
}
7073

7174
override func layoutSubviews()

Sources/ASCollectionView/Cells/ASCollectionViewSupplementaryView.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@ class ASCollectionViewSupplementaryView: UICollectionReusableView
5353
}
5454
}
5555

56+
var shouldSkipNextRefresh: Bool = true
5657
override func prepareForReuse()
5758
{
5859
hostingController = nil
60+
shouldSkipNextRefresh = true
5961
}
6062

6163
override func layoutSubviews()

Sources/ASCollectionView/Cells/ASTableViewCell.swift

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ class ASTableViewCell: UITableViewCell, ASDataSourceConfigurableCell
1515
{
1616
hostingController?.invalidateCellLayoutCallback = invalidateLayoutCallback
1717
hostingController?.tableViewScrollToCellCallback = scrollToCellCallback
18+
if hostingController !== oldValue, hostingController != nil
19+
{
20+
attachView()
21+
}
1822
}
1923
}
2024

@@ -39,22 +43,23 @@ class ASTableViewCell: UITableViewCell, ASDataSourceConfigurableCell
3943
{
4044
hostingController?.viewController.removeFromParent()
4145
hostingController.map { vc.addChild($0.viewController) }
42-
attachView()
4346
hostingController?.viewController.didMove(toParent: vc)
4447
}
45-
else
46-
{
47-
attachView()
48-
}
4948
}
5049

5150
func didDisappear()
5251
{
5352
hostingController?.viewController.removeFromParent()
5453
}
5554

55+
override func didMoveToSuperview()
56+
{
57+
attachView()
58+
}
59+
5660
private func attachView()
5761
{
62+
guard superview != nil else { return }
5863
guard let hcView = hostingController?.viewController.view else
5964
{
6065
contentView.subviews.forEach { $0.removeFromSuperview() }
@@ -68,13 +73,15 @@ class ASTableViewCell: UITableViewCell, ASDataSourceConfigurableCell
6873
}
6974
}
7075

76+
var shouldSkipNextRefresh: Bool = true // This is used to avoid double-up in reloaded cells and our update from swiftUI
7177
override func prepareForReuse()
7278
{
7379
backgroundColor = nil
7480
indexPath = nil
7581
itemID = nil
7682
hostingController = nil
7783
isSelected = false
84+
shouldSkipNextRefresh = true
7885
}
7986

8087
func recalculateSize()

Sources/ASCollectionView/Cells/ASTableViewSupplementaryView.swift

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ class ASTableViewSupplementaryView: UITableViewHeaderFooterView
1111
{
1212
didSet
1313
{
14-
setNeedsLayout()
14+
if hostingController !== oldValue, hostingController != nil
15+
{
16+
attachView()
17+
}
1518
}
1619
}
1720

1821
var sectionIDHash: Int?
22+
var supplementaryKind: String?
1923

2024
override init(reuseIdentifier: String?)
2125
{
@@ -40,22 +44,23 @@ class ASTableViewSupplementaryView: UITableViewHeaderFooterView
4044
{
4145
hostingController?.viewController.removeFromParent()
4246
hostingController.map { vc.addChild($0.viewController) }
43-
attachView()
4447
hostingController?.viewController.didMove(toParent: vc)
4548
}
46-
else
47-
{
48-
attachView()
49-
}
5049
}
5150

5251
func didDisappear()
5352
{
5453
hostingController?.viewController.removeFromParent()
5554
}
5655

56+
override func didMoveToSuperview()
57+
{
58+
attachView()
59+
}
60+
5761
private func attachView()
5862
{
63+
guard superview != nil else { return }
5964
guard let hcView = hostingController?.viewController.view else
6065
{
6166
contentView.subviews.forEach { $0.removeFromSuperview() }
@@ -69,10 +74,12 @@ class ASTableViewSupplementaryView: UITableViewHeaderFooterView
6974
}
7075
}
7176

77+
var shouldSkipNextRefresh: Bool = true
7278
override func prepareForReuse()
7379
{
7480
hostingController = nil
7581
sectionIDHash = nil
82+
shouldSkipNextRefresh = true
7683
}
7784

7885
override func layoutSubviews()

Sources/ASCollectionView/Datasource/ASDiffableDataSourceTableView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ class ASDiffableDataSourceTableView<SectionID: Hashable>: ASDiffableDataSource<S
4242
{
4343
CATransaction.setDisableActions(true)
4444
}
45-
CATransaction.setCompletionBlock({ [weak self] in
45+
CATransaction.setCompletionBlock { [weak self] in
4646
self?.canRefreshSizes = true
4747
completion?()
48-
})
48+
}
4949
tableView.reload(using: changeset, with: .none) { newSections in
5050
self.currentSnapshot = .init(sections: newSections)
5151
}

Sources/ASCollectionView/FunctionBuilders/ViewArrayBuilder.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public struct ViewArrayBuilder
8787
{
8888
.group([Wrapper(header), .group(array.map { Wrapper($0) })])
8989
}
90-
90+
9191
public static func buildBlock<C0: View, CX: View, C1: View>(_ header: C0, _ array: [CX], _ footer: C1) -> Output
9292
{
9393
.group([Wrapper(header), .group(array.map { Wrapper($0) }), Wrapper(footer)])

Sources/ASCollectionView/Implementation/ASCollectionView.swift

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentab
8181
{
8282
context.coordinator.parent = self
8383
context.coordinator.updateCollectionViewSettings(collectionViewController.collectionView)
84-
context.coordinator.updateContent(collectionViewController.collectionView, transaction: context.transaction, refreshExistingCells: true)
84+
context.coordinator.updateContent(collectionViewController.collectionView, transaction: context.transaction)
8585
context.coordinator.updateLayout()
8686
context.coordinator.configureRefreshControl(for: collectionViewController.collectionView)
8787
#if DEBUG
@@ -275,7 +275,7 @@ public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentab
275275
setupPrefetching()
276276
}
277277

278-
func populateDataSource(animated: Bool = true)
278+
func populateDataSource(animated: Bool = true, transaction: Transaction? = nil)
279279
{
280280
guard hasMovedToParent else { return }
281281
collectionViewController.map { registerSupplementaries(forCollectionView: $0.collectionView) } // New sections might involve new types of supplementary...
@@ -285,22 +285,21 @@ public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentab
285285
}
286286
)
287287
dataSource?.applySnapshot(snapshot, animated: animated)
288+
withAnimation(animated ? transaction?.animation : nil) {
289+
refreshVisibleCells()
290+
}
288291
collectionViewController.map { self.didUpdateContentSize($0.collectionView.contentSize) }
289292
}
290293

291-
func updateContent(_ cv: UICollectionView, transaction: Transaction?, refreshExistingCells: Bool)
294+
func updateContent(_ cv: UICollectionView, transaction: Transaction?)
292295
{
293296
guard hasMovedToParent else { return }
294297

295298
let transactionAnimationEnabled = (transaction?.animation != nil) && !(transaction?.disablesAnimations ?? false)
296-
populateDataSource(animated: parent.animateOnDataRefresh && transactionAnimationEnabled)
299+
populateDataSource(
300+
animated: parent.animateOnDataRefresh && transactionAnimationEnabled,
301+
transaction: transaction)
297302

298-
if refreshExistingCells
299-
{
300-
withAnimation(parent.animateOnDataRefresh ? transaction?.animation : nil) {
301-
refreshVisibleCells()
302-
}
303-
}
304303
updateSelectionBindings(cv)
305304
}
306305

@@ -309,27 +308,30 @@ public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentab
309308
guard let cv = collectionViewController?.collectionView else { return }
310309
for case let cell as Cell in cv.visibleCells
311310
{
311+
guard !cell.shouldSkipNextRefresh else { cell.shouldSkipNextRefresh = false; continue }
312312
guard
313313
let itemID = cell.itemID,
314314
let hc = cell.hostingController
315-
else { return }
315+
else { continue }
316316
self.section(forItemID: itemID)?.dataSource.update(hc, forItemID: itemID)
317317
}
318318

319319
supplementaryKinds().forEach
320320
{ kind in
321-
cv.indexPathsForVisibleSupplementaryElements(ofKind: kind).forEach
321+
for indexPath in cv.indexPathsForVisibleSupplementaryElements(ofKind: kind)
322322
{
323-
guard let view = (cv.supplementaryView(forElementKind: kind, at: $0) as? ASCollectionViewSupplementaryView) else { return }
324-
view.hostingController = parent.sections[safe: $0.section]?.dataSource.updateOrCreateHostController(forSupplementaryKind: kind, existingHC: view.hostingController)
323+
guard let view = (cv.supplementaryView(forElementKind: kind, at: indexPath) as? ASCollectionViewSupplementaryView) else { continue }
324+
guard !view.shouldSkipNextRefresh else { view.shouldSkipNextRefresh = false; continue }
325+
326+
view.hostingController = parent.sections[safe: indexPath.section]?.dataSource.updateOrCreateHostController(forSupplementaryKind: kind, existingHC: view.hostingController)
325327
}
326328
}
327329
}
328330

329331
func onMoveToParent()
330332
{
331333
guard !hasMovedToParent else { return }
332-
334+
333335
hasMovedToParent = true
334336
populateDataSource(animated: false)
335337
}
@@ -488,7 +490,10 @@ public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentab
488490
usingSpringWithDamping: 1.0,
489491
initialSpringVelocity: 0.0,
490492
options: UIView.AnimationOptions(),
491-
animations: changes,
493+
animations: {
494+
changes()
495+
collectionViewController.collectionView.layoutIfNeeded()
496+
},
492497
completion: nil)
493498
}
494499
else

Sources/ASCollectionView/Implementation/ASSectionDataSource.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ internal protocol ASSectionDataSourceProtocol
1313
func updateOrCreateHostController(forItemID itemID: ASCollectionViewItemUniqueID, existingHC: ASHostingControllerProtocol?) -> ASHostingControllerProtocol?
1414
func update(_ hc: ASHostingControllerProtocol, forItemID itemID: ASCollectionViewItemUniqueID)
1515
func updateOrCreateHostController(forSupplementaryKind supplementaryKind: String, existingHC: ASHostingControllerProtocol?) -> ASHostingControllerProtocol?
16-
func update(_ hc: ASHostingControllerProtocol, forSupplementaryKind supplementaryKind: String)
1716
var supplementaryViews: [String: AnyView] { get set }
1817
func getTypeErasedData(for indexPath: IndexPath) -> Any?
1918
func onAppear(_ indexPath: IndexPath)
@@ -108,12 +107,6 @@ internal struct ASSectionDataSource<DataCollection: RandomAccessCollection, Data
108107
return updateOrCreateHostController(content: content, existingHC: existingHC)
109108
}
110109

111-
func update(_ hc: ASHostingControllerProtocol, forSupplementaryKind supplementaryKind: String)
112-
{
113-
guard let content = supplementaryViews[supplementaryKind] else { return }
114-
update(hc, withContent: content)
115-
}
116-
117110
private func updateOrCreateHostController<Wrapped: View>(content: Wrapped, existingHC: ASHostingControllerProtocol?) -> ASHostingControllerProtocol
118111
{
119112
if let hc = (existingHC as? ASHostingController<Wrapped>)

0 commit comments

Comments
 (0)