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

Commit

Permalink
V1.6.1 (#140)
Browse files Browse the repository at this point in the history
* Further refinement of cell lifecycle

* Workaround UICollectionViewCell bug

* Fix for animating layoutChange when `shouldInvalidateLayoutOnStateChange` is enabled
  • Loading branch information
apptekstudios authored Apr 30, 2020
1 parent 88a0ecf commit 44e46a9
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 107 deletions.
3 changes: 3 additions & 0 deletions Sources/ASCollectionView/Cells/ASCollectionViewCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,15 @@ class ASCollectionViewCell: UICollectionViewCell, ASDataSourceConfigurableCell
}
}

var shouldSkipNextRefresh: Bool = true

override func prepareForReuse()
{
indexPath = nil
itemID = nil
isSelected = false
hostingController = nil
shouldSkipNextRefresh = true
}

override func layoutSubviews()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ class ASCollectionViewSupplementaryView: UICollectionReusableView
}
}

var shouldSkipNextRefresh: Bool = true
override func prepareForReuse()
{
hostingController = nil
shouldSkipNextRefresh = true
}

override func layoutSubviews()
Expand Down
17 changes: 12 additions & 5 deletions Sources/ASCollectionView/Cells/ASTableViewCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ class ASTableViewCell: UITableViewCell, ASDataSourceConfigurableCell
{
hostingController?.invalidateCellLayoutCallback = invalidateLayoutCallback
hostingController?.tableViewScrollToCellCallback = scrollToCellCallback
if hostingController !== oldValue, hostingController != nil
{
attachView()
}
}
}

Expand All @@ -39,22 +43,23 @@ class ASTableViewCell: UITableViewCell, ASDataSourceConfigurableCell
{
hostingController?.viewController.removeFromParent()
hostingController.map { vc.addChild($0.viewController) }
attachView()
hostingController?.viewController.didMove(toParent: vc)
}
else
{
attachView()
}
}

func didDisappear()
{
hostingController?.viewController.removeFromParent()
}

override func didMoveToSuperview()
{
attachView()
}

private func attachView()
{
guard superview != nil else { return }
guard let hcView = hostingController?.viewController.view else
{
contentView.subviews.forEach { $0.removeFromSuperview() }
Expand All @@ -68,13 +73,15 @@ class ASTableViewCell: UITableViewCell, ASDataSourceConfigurableCell
}
}

var shouldSkipNextRefresh: Bool = true // This is used to avoid double-up in reloaded cells and our update from swiftUI
override func prepareForReuse()
{
backgroundColor = nil
indexPath = nil
itemID = nil
hostingController = nil
isSelected = false
shouldSkipNextRefresh = true
}

func recalculateSize()
Expand Down
19 changes: 13 additions & 6 deletions Sources/ASCollectionView/Cells/ASTableViewSupplementaryView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ class ASTableViewSupplementaryView: UITableViewHeaderFooterView
{
didSet
{
setNeedsLayout()
if hostingController !== oldValue, hostingController != nil
{
attachView()
}
}
}

var sectionIDHash: Int?
var supplementaryKind: String?

override init(reuseIdentifier: String?)
{
Expand All @@ -40,22 +44,23 @@ class ASTableViewSupplementaryView: UITableViewHeaderFooterView
{
hostingController?.viewController.removeFromParent()
hostingController.map { vc.addChild($0.viewController) }
attachView()
hostingController?.viewController.didMove(toParent: vc)
}
else
{
attachView()
}
}

func didDisappear()
{
hostingController?.viewController.removeFromParent()
}

override func didMoveToSuperview()
{
attachView()
}

private func attachView()
{
guard superview != nil else { return }
guard let hcView = hostingController?.viewController.view else
{
contentView.subviews.forEach { $0.removeFromSuperview() }
Expand All @@ -69,10 +74,12 @@ class ASTableViewSupplementaryView: UITableViewHeaderFooterView
}
}

var shouldSkipNextRefresh: Bool = true
override func prepareForReuse()
{
hostingController = nil
sectionIDHash = nil
shouldSkipNextRefresh = true
}

override func layoutSubviews()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ class ASDiffableDataSourceTableView<SectionID: Hashable>: ASDiffableDataSource<S
{
CATransaction.setDisableActions(true)
}
CATransaction.setCompletionBlock({ [weak self] in
CATransaction.setCompletionBlock { [weak self] in
self?.canRefreshSizes = true
completion?()
})
}
tableView.reload(using: changeset, with: .none) { newSections in
self.currentSnapshot = .init(sections: newSections)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public struct ViewArrayBuilder
{
.group([Wrapper(header), .group(array.map { Wrapper($0) })])
}

public static func buildBlock<C0: View, CX: View, C1: View>(_ header: C0, _ array: [CX], _ footer: C1) -> Output
{
.group([Wrapper(header), .group(array.map { Wrapper($0) }), Wrapper(footer)])
Expand Down
37 changes: 21 additions & 16 deletions Sources/ASCollectionView/Implementation/ASCollectionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentab
{
context.coordinator.parent = self
context.coordinator.updateCollectionViewSettings(collectionViewController.collectionView)
context.coordinator.updateContent(collectionViewController.collectionView, transaction: context.transaction, refreshExistingCells: true)
context.coordinator.updateContent(collectionViewController.collectionView, transaction: context.transaction)
context.coordinator.updateLayout()
context.coordinator.configureRefreshControl(for: collectionViewController.collectionView)
#if DEBUG
Expand Down Expand Up @@ -275,7 +275,7 @@ public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentab
setupPrefetching()
}

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

func updateContent(_ cv: UICollectionView, transaction: Transaction?, refreshExistingCells: Bool)
func updateContent(_ cv: UICollectionView, transaction: Transaction?)
{
guard hasMovedToParent else { return }

let transactionAnimationEnabled = (transaction?.animation != nil) && !(transaction?.disablesAnimations ?? false)
populateDataSource(animated: parent.animateOnDataRefresh && transactionAnimationEnabled)
populateDataSource(
animated: parent.animateOnDataRefresh && transactionAnimationEnabled,
transaction: transaction)

if refreshExistingCells
{
withAnimation(parent.animateOnDataRefresh ? transaction?.animation : nil) {
refreshVisibleCells()
}
}
updateSelectionBindings(cv)
}

Expand All @@ -309,27 +308,30 @@ public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentab
guard let cv = collectionViewController?.collectionView else { return }
for case let cell as Cell in cv.visibleCells
{
guard !cell.shouldSkipNextRefresh else { cell.shouldSkipNextRefresh = false; continue }
guard
let itemID = cell.itemID,
let hc = cell.hostingController
else { return }
else { continue }
self.section(forItemID: itemID)?.dataSource.update(hc, forItemID: itemID)
}

supplementaryKinds().forEach
{ kind in
cv.indexPathsForVisibleSupplementaryElements(ofKind: kind).forEach
for indexPath in cv.indexPathsForVisibleSupplementaryElements(ofKind: kind)
{
guard let view = (cv.supplementaryView(forElementKind: kind, at: $0) as? ASCollectionViewSupplementaryView) else { return }
view.hostingController = parent.sections[safe: $0.section]?.dataSource.updateOrCreateHostController(forSupplementaryKind: kind, existingHC: view.hostingController)
guard let view = (cv.supplementaryView(forElementKind: kind, at: indexPath) as? ASCollectionViewSupplementaryView) else { continue }
guard !view.shouldSkipNextRefresh else { view.shouldSkipNextRefresh = false; continue }

view.hostingController = parent.sections[safe: indexPath.section]?.dataSource.updateOrCreateHostController(forSupplementaryKind: kind, existingHC: view.hostingController)
}
}
}

func onMoveToParent()
{
guard !hasMovedToParent else { return }

hasMovedToParent = true
populateDataSource(animated: false)
}
Expand Down Expand Up @@ -488,7 +490,10 @@ public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentab
usingSpringWithDamping: 1.0,
initialSpringVelocity: 0.0,
options: UIView.AnimationOptions(),
animations: changes,
animations: {
changes()
collectionViewController.collectionView.layoutIfNeeded()
},
completion: nil)
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ internal protocol ASSectionDataSourceProtocol
func updateOrCreateHostController(forItemID itemID: ASCollectionViewItemUniqueID, existingHC: ASHostingControllerProtocol?) -> ASHostingControllerProtocol?
func update(_ hc: ASHostingControllerProtocol, forItemID itemID: ASCollectionViewItemUniqueID)
func updateOrCreateHostController(forSupplementaryKind supplementaryKind: String, existingHC: ASHostingControllerProtocol?) -> ASHostingControllerProtocol?
func update(_ hc: ASHostingControllerProtocol, forSupplementaryKind supplementaryKind: String)
var supplementaryViews: [String: AnyView] { get set }
func getTypeErasedData(for indexPath: IndexPath) -> Any?
func onAppear(_ indexPath: IndexPath)
Expand Down Expand Up @@ -108,12 +107,6 @@ internal struct ASSectionDataSource<DataCollection: RandomAccessCollection, Data
return updateOrCreateHostController(content: content, existingHC: existingHC)
}

func update(_ hc: ASHostingControllerProtocol, forSupplementaryKind supplementaryKind: String)
{
guard let content = supplementaryViews[supplementaryKind] else { return }
update(hc, withContent: content)
}

private func updateOrCreateHostController<Wrapped: View>(content: Wrapped, existingHC: ASHostingControllerProtocol?) -> ASHostingControllerProtocol
{
if let hc = (existingHC as? ASHostingController<Wrapped>)
Expand Down
Loading

0 comments on commit 44e46a9

Please sign in to comment.