How you can Detect Scroll Completion Earlier than Calculating Body in Swift (iOS)?
I’m engaged on an iOS software the place I’ve a display screen 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 characteristic utilizing EasyTipView, the place I spotlight particular cells in each the UICollectionView and UITableView. The movement 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 cross it to comletion.
Downside
The body calculation is going on too early, inflicting incorrect placements of suggestions. I’m presently utilizing DispatchQueue.major.asyncAfter with a delay to attend for the scrolling to complete, however this method is unreliable as a result of:
• Completely different gadgets have completely different scrolling speeds.
• Scrolling to distant cells takes longer.
Anticipated Habits
I wish to detect when scrolling is totally accomplished, making certain 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)
}
non-public func handleScrollCompletion(for scrollView: UIScrollView, completion: (() -> Void)? = nil) {
if scrollView == tableView, isTableViewScrolling {
isTableViewScrolling = false
completion?()
} else if scrollView == collectionView, isCollectionViewScrolling {
isCollectionViewScrolling = false
completion?()
}
}
non-public func checkIfScrollFinished(for scrollView: UIScrollView, completion: (() -> Void)? = nil) {
DispatchQueue.major.async { [weak self] in
guard let self = self else { return }
if !scrollView.isDragging && !scrollView.isDecelerating {
DispatchQueue.major.asyncAfter(deadline: .now() + 0.3) {
self.handleScrollCompletion(for: scrollView, completion: completion)
}
}
}
}
CollectionView Logic:
non-public func scrollToTabAndGetFrame(for selectedTab: tabData, completion: ((CGRect?) -> Void)? = nil) {
isCollectionViewScrolling = true
viewModel?.selectedTab = selectedTab // -> Scrolls robotically when selectedTab adjustments
DispatchQueue.major.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.major.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 totally accomplished, making certain the cell is correctly seen earlier than calculating its body?
Is there a greater method than utilizing scrollViewDidEndScrollingAnimation and scrollViewDidEndDecelerating?
Any ideas for bettering this implementation could be significantly appreciated!