How to Improve UITableView and UICollectionView Cell Deselection with Swift Code Examples
Download materialsEvery iOS developer recognizes the importance of fine-tuning and polishing the user interface to provide a seamless, intuitive, and engaging user experience. One aspect that sometimes receives less attention, but can make a significant difference in UI fluency, is the proper handling of cell deselection in UITableView and UICollectionView when transitioning between views. When executed well, this not only lends a professional touch to your application but also enhances its functionality.
In this article, we'll explore how to properly deselect cells during transitions, replicating the native SwiftUI NavigationLink deselection behavior in UIKit. Additionally, I'll provide a handy UIViewController extension that can be readily incorporated into your apps. This will assist in making your code cleaner and more efficient. If you aim to give your apps a polished, professional feel, then keep reading.
Handling Cell Deselection in UICollectionView
Many apps display lists – be it tasks, notes, settings, or something else. The built-in behavior of a UITableView
or UICollectionView
is to highlight a cell when it's tapped. We typically navigate from this tap to a detail screen, and it's often desired to deselect the cell when the user returns to the list screen, indicating what item was previously selected.
A straightforward implementation could look like this:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let selectedIndexPath = collectionView.indexPathsForSelectedItems?.first {
collectionView.deselectItem(at: selectedIndexPath, animated: true)
}
}
While this approach works, it's not ideal. The deselection animation doesn't sync with the screen transition and doesn't cover a scenario where the user starts a swipe-back gesture but decides not to go back. This is where the transitionCoordinator
comes into play - a feature used by default in SwiftUI
and in Apple's apps.
Improving Deselection with Transition Coordinator
UIViewController
has a transitionCoordinator: UIViewControllerTransitionCoordinator?
property, which is not nil
during an active transition. The following code deselects the cell in sync with the transition and reselects it if the transition gets cancelled:
transitionCoordinator?.animate(alongsideTransition: { context in
collectionView.deselectItem(at: indexPath, animated: true)}) { context in
if context.isCancelled {
collectionView.selectItem(at: indexPath, animated: false, scrollPosition: [])
}
}
To simplify its use, we can encapsulate this into an extension:
extension UIViewController {
func deselectItem(withTransition: Bool = true, collectionView: UICollectionView?) {
guard let collectionView = collectionView else { return }
if let indexPath = collectionView.indexPathsForSelectedItems?.first {
if withTransition, let coordinator = transitionCoordinator {
coordinator.animate(alongsideTransition: { context in
collectionView.deselectItem(at: indexPath, animated: true)}) { context in
if context.isCancelled {
collectionView.selectItem(at: indexPath, animated: false, scrollPosition: [])
}
}
} else {
collectionView.deselectItem(at: indexPath, animated: true)
}
}
}
}
With this in place, we can call this method in viewWillAppear()
or other relevant places:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
deselectItem(collectionView: collectionView)
}
This new approach provides a more synchronized and smooth cell deselection that aligns with best practices, delivering a polished, professional user experience.
Conclusion
In the world of iOS development, it's often the fine details that set apart good apps from great ones. Properly handling cell deselection in UITableView
and UICollectionView
is one of those details. This small feature contributes to the overall user experience in a meaningful way.
In this article, we've explored how to mimic SwiftUI
's native NavigationLink
deselection behaviour in UIKit
, enhancing the transition between different views. The key to achieving this is the transitionCoordinator
, which synchronizes the deselection animation with screen transitions and handles the scenario where users change their mind mid-swipe. By implementing the UIViewController
extension provided, you can incorporate this technique into your apps.
I hope you enjoyed this article. If you have any questions, suggestions, or feedback, please let me know on Twitter.
You can download a sample project with the implementation via the link at the top of the page.
Thanks for reading!