Reputation: 16092
I've added a UICollectionView as a subview to my ViewController. I've also set a UIRefreshControl for the UICollectionView which applies a NSDiffableDataSourceSnapshot when I pull down to refresh. The animation jumps when applying the snapshot (note this happens even when the navigator bar title isn't large.)
Here are the relevant snippets of code
var collectionView: UICollectionView! = nil
var dataSource: UICollectionViewDiffableDataSource<Section, Item>! = nil
override func viewDidLoad() {
super.viewDidLoad()
let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout:
generateLayout())
view.addSubview(collectionView)
collectionView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
collectionView.backgroundColor = .systemGroupedBackground
self.collectionView = collectionView
collectionView.delegate = self
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(doSomething), for: .valueChanged)
collectionView.refreshControl = refreshControl
}
@objc func doSomething(refreshControl: UIRefreshControl) {
DispatchQueue.main.async {
var dataSourceSnapshot = NSDiffableDataSourceSnapshot<Section, Item>()
// add data
dataSource.apply(dataSourceSnapshot)
}
refreshControl.endRefreshing()
}
Are there any changes I can make to apply the snapshot without the sudden jump?
Upvotes: 3
Views: 1970
Reputation: 1867
Unfortunately Apple Engineers can not properly solve that issue since iOS 6.
Long story short: you don't want to endRefreshing
while your scroll view isTracking
. UIRefreshControl
modifies contentInset
and contentOffset
of the scrollView, and it always caused jumps like that.
If you have a network request state anywhere, or any other flag indicating that the loading is in progress you could simply do:
@objc func doSomething(refreshControl: UIRefreshControl) {
loading = true
DispatchQueue.main.async {
var dataSourceSnapshot = NSDiffableDataSourceSnapshot<Section, Item>()
// add data
dataSourceSnapshot.appendSections([.test])
dataSourceSnapshot.appendItems([.test])
self.dataSource.apply(dataSourceSnapshot)
self.loading = false
}
}
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !loading && collectionView.refreshControl!.isRefreshing {
collectionView.refreshControl!.endRefreshing()
}
}
I don't quite like loading
flags like that, but that's just an example. Hopefully you have better source of truth indicating if some "work" is still in progress.
I guess there might be a nicer way of solving it (by patching classes in runtime), but it usually takes time to investigate. I wonder if that solution satisfies your needs.
Upvotes: 2