Methods to Detect Scroll Completion Earlier than Calculating Body in Swift (iOS)?
I’m engaged on an iOS software the place I’ve a display containing each a UICollectionView and a UITableView. The UICollectionView acts as a tab selector, and when an merchandise is chosen, the UITableView updates its content material accordingly.
I’m implementing an onboarding/tutorial function utilizing EasyTipView, the place I spotlight particular cells in each the UICollectionView and UITableView. The move works as follows:
1. Present the primary tip.
2. Scroll the UICollectionView to pick out a tab.
3. As soon as the tab is chosen, replace the UITableView and scroll to the corresponding cell if wanted.
4. Discover the body of the goal cell and go it to comletion.
Downside
The body calculation is going on too early, inflicting incorrect placements of ideas. I’m presently utilizing DispatchQueue.principal.asyncAfter with a delay to attend for the scrolling to complete, however this method is unreliable as a result of:
• Completely different units have totally different scrolling speeds.
• Scrolling to distant cells takes longer.
Anticipated Habits
I wish to detect when scrolling is absolutely accomplished, guaranteeing that the body of the goal cell is correct earlier than passing it to EasyTipView.
Present Try
I’m utilizing scrollViewDidEndScrollingAnimation and scrollViewDidEndDecelerating to detect when scrolling stops:
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
handleScrollCompletion(for: scrollView)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
handleScrollCompletion(for: scrollView)
}
personal func handleScrollCompletion(for scrollView: UIScrollView, completion: (() -> Void)? = nil) {
if scrollView == tableView, isTableViewScrolling {
isTableViewScrolling = false
completion?()
} else if scrollView == collectionView, isCollectionViewScrolling {
isCollectionViewScrolling = false
completion?()
}
}
personal func checkIfScrollFinished(for scrollView: UIScrollView, completion: (() -> Void)? = nil) {
DispatchQueue.principal.async { [weak self] in
guard let self = self else { return }
if !scrollView.isDragging && !scrollView.isDecelerating {
DispatchQueue.principal.asyncAfter(deadline: .now() + 0.3) {
self.handleScrollCompletion(for: scrollView, completion: completion)
}
}
}
}
CollectionView Logic:
personal func scrollToTabAndGetFrame(for selectedTab: tabData, completion: ((CGRect?) -> Void)? = nil) {
isCollectionViewScrolling = true
viewModel?.selectedTab = selectedTab // -> Scrolls mechanically when selectedTab modifications
DispatchQueue.principal.async {
self.collectionView.layoutIfNeeded()
self.checkIfScrollFinished(for: self.collectionView) { [weak self] in
guard let self = self else { return }
guard let index = self.viewModel?.tabData.firstIndex(the place: { $0 == selectedTab }) else { return }
let indexPath = IndexPath(row: index, part: 0)
if let cell = self.collectionView.cellForItem(at: indexPath) {
let cellFrame = self.collectionView.convert(cell.body, to: self.view)
completion?(cellFrame)
} else {
completion?(nil)
}
}
}
}
TableView:
func scrollToCellAndGetFrame(for selectedTab: tabData, for cell: cellData, completion: ((CGRect?) -> Void)? = nil) {
isTableViewScrolling = true
viewModel?.selectedTab = selectedTab
DispatchQueue.principal.async {
self.collectionView.layoutIfNeeded()
self.checkIfScrollFinished(for: self.collectionView) { [weak self] in
guard let self = self else { return }
guard let index = self.viewModel?.tabSections.firstIndex(the place: { $0 == sectionType }) else { return }
let indexPath = IndexPath(row: index, part: 0)
self.tableView.scrollToRow(at: indexPath, at: .center, animated: true)
self.checkIfScrollFinished(for: self.tableView) { [weak self] in
guard let self = self else { return }
if let cell = self.tableView.cellForRow(at: indexPath) {
let cellFrame = self.tableView.convert(cell.body, to: self.view)
completion?(cellFrame)
} else {
completion?(nil)
}
}
}
}
}
How can I reliably detect when each UICollectionView and UITableView scrolling is absolutely accomplished, guaranteeing the cell is correctly seen earlier than calculating its body?
Is there a greater method than utilizing scrollViewDidEndScrollingAnimation and scrollViewDidEndDecelerating?
Any recommendations for bettering this implementation could be vastly appreciated!