The timing and effect of deselectRow in tableView is very special. Normally our deselect operation will be performed in the didSelect proxy method, or more specifically, in viewDidAppear.
But the iOS native App says no, I can do better, and here’s what it looks like in the system Settings:
The deselect animation will change with the progress of the sliding gesture when you slide back. After searching, there are not many articles about it in China, and the few software I use frequently do not have a similar effect.
Search after The record is rarely found abroad, only three articles to record The interaction, which more detailed written Diary # 17 – is this The Hit List are clearsSelectionOnViewWillAppear.
The abstract transitionCoordinator of transition animation
This interaction is achieved by UIViewController transitionCoordinator attributes, it is of type UIViewControllerTransitionCoordinator.
To put it simply, it can help us to add some custom animations into the transition animation. The progress and life cycle of the custom animation will be consistent with the transition animation, and it can achieve a more natural and consistent transition effect, such as the change of navigationBar background color in push animation. It provides these methods to register the callback of the animation life cycle:
protocol UIViewControllerTransitionCoordinator {
func animate(
alongsideTransition animation: ((UIViewControllerTransitionCoordinatorContext) -> Void)? , completion: ((UIViewControllerTransitionCoordinatorContext) - >Void)? = nil) - >Bool
func animateAlongsideTransition(
inview: UIView? , animation:((UIViewControllerTransitionCoordinatorContext) -> Void)? , completion: ((UIViewControllerTransitionCoordinatorContext) - >Void)? = nil) - >Bool
func notifyWhenInteractionChanges(
_ handler: @escaping (UIViewControllerTransitionCoordinatorContext) -> Void)}Copy the code
Recommend you to have a look at UIViewControllerTransitionCoordinator document in this agreement, this excerpt a I think the more interesting description:
Using the transition coordinator to handle view hierarchy animations is preferred over making those same changes in the viewWillAppear(_:) or similar methods of your view controllers. The blocks you register with the methods of this protocol are guaranteed to execute at the same time as the transition animations. More importantly, the transition coordinator provides important information about the state of the transition, such as whether it was cancelled, to your animation blocks through the UIViewControllerTransitionCoordinatorContext object.
TransitionCoordinator is recommended to handle view-level animations rather than viewWillAppear and other similar ViewController lifecycle functions. The function you register is guaranteed to be executed at the same time as the transition animation. And, more importantly, through transitionCoordinator UIViewControllerTransitionCoordinatorContext agreement to provide the state transitions of animation and other important information, such as whether the animation has been cancelled.
The first thing that comes to my mind is navigationBar. Properties such as barTintColor can be used to animate transitions more naturally using transitionCoordinator.
Implementation and encapsulation
After looking at other people’s articles and trying other centralization methods, I felt that the best time for transitionCoordinator access would be viewWillAppear. The logic would look something like this:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Determine if a Row is selected
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Check whether a transitionCoordinator exists
if let coordinator = transitionCoordinator {
// In some cases, an Animation block is registered through a coordinator
coordinator.animate(
alongsideTransition: { _ in
self.tableView.deselectRow(at: selectedIndexPath, animated: true)
},
completion: { context in
// If the transition animation is cancelled, you need to return the tableView to the selected state
guard context.isCancelled else { return }
self.tableView.selectRow(at: selectedIndexPath, animated: true, scrollPosition: .none)})}else {
// No direct deselect
tableView.deselectRow(at: selectedIndexPath, animated: animated)
}
}
}
Copy the code
If the transitionCoordinator is simply an animation abstraction (leaving transitions behind), the operation we want to follow the animation through is deselect, We can further encapsulate the deselect operation into the UITableView extension:
extension UITableView {
public func deselectRowIfNeeded(with transitionCoordinator: UIViewControllerTransitionCoordinator? , animated: Bool) {
guard let selectedIndexPath = selectRowAtIndexPath else { return }
guard let coordinator = transitionCoordinator else {
self.deselectRow(at: selectedIndexPath, animated: animated)
return
}
coordinator.animate(
alongsideTransition: { _ in
self.deselectRow(at: selectedIndexPath, animated: true)
},
completion: { context in
guard context.isCancelled else { return }
self.selectRow(at: selectedIndexPath, animated: false, scrollPosition: .none)})}}Copy the code
And then just call it in viewWillAppear:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.deselectRowIfNeeded(with: transitionCoordinator, animated: true)}Copy the code
If you package your Own TableViewController in your project and use it properly, it’s easy to add this effect.
conclusion
This is the complete example.
Reference links:
- UIViewControllerTransitionCoordinator official documentation
- The Hit List Diary # 17 – clearsSelectionOnViewWillAppear
- IOS – navigation bar transparency and barTintColor gradient transition
If you think the article is good, you can follow my blog