Last active
January 15, 2023 13:03
-
-
Save smileyborg/ec4812c146f575cd006d98d681108ba8 to your computer and use it in GitHub Desktop.
Animate table & collection view deselection alongside interactive transition (for iOS 11 and later)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Starting in iOS 11, interactive view controller transitions no longer scrub by setting the layer speed to zero | |
and changing the timeOffset. As a result of this change, implicit animations that occur in places like | |
-viewWillAppear: (called during an interactive transition) no longer end up “caught in” the animation. | |
To get the same behavior for table view row deselection as before, you can either use UITableViewController | |
which implements this for you, or you can implement it manually by deselecting the row in an alongside | |
animation for the transition (set up in -viewWillAppear: using the transition coordinator). | |
The example implementations here correctly handle some of the more subtle corner cases. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// UICollectionView Objective-C example | |
- (void)viewWillAppear:(BOOL)animated { | |
[super viewWillAppear:animated]; | |
NSIndexPath *selectedIndexPath = [[self.collectionView indexPathsForSelectedItems] firstObject]; | |
if (selectedIndexPath != nil) { | |
id<UIViewControllerTransitionCoordinator> coordinator = self.transitionCoordinator; | |
if (coordinator != nil) { | |
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) { | |
[self.collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES]; | |
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) { | |
if (context.cancelled) { | |
[self.collectionView selectItemAtIndexPath:selectedIndexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; | |
} | |
}]; | |
} else { | |
[self.collectionView deselectItemAtIndexPath:selectedIndexPath animated:animated]; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// UICollectionView Swift example | |
override func viewWillAppear(_ animated: Bool) { | |
super.viewWillAppear(animated) | |
if let indexPath = collectionView.indexPathsForSelectedItems?.first { | |
if let coordinator = transitionCoordinator { | |
coordinator.animate(alongsideTransition: { _ in | |
self.collectionView.deselectItem(at: indexPath, animated: true) | |
}, completion: { context in | |
if context.isCancelled { | |
self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: []) | |
} | |
}) | |
} else { | |
collectionView.deselectItem(at: indexPath, animated: animated) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// UITableView Objective-C example | |
- (void)viewWillAppear:(BOOL)animated { | |
[super viewWillAppear:animated]; | |
NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow]; | |
if (selectedIndexPath != nil) { | |
id<UIViewControllerTransitionCoordinator> coordinator = self.transitionCoordinator; | |
if (coordinator != nil) { | |
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) { | |
[self.tableView deselectRowAtIndexPath:selectedIndexPath animated:YES]; | |
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) { | |
if (context.cancelled) { | |
[self.tableView selectRowAtIndexPath:selectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; | |
} | |
}]; | |
} else { | |
[self.tableView deselectRowAtIndexPath:selectedIndexPath animated:animated]; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// UITableView Swift example | |
override func viewWillAppear(_ animated: Bool) { | |
super.viewWillAppear(animated) | |
if let selectedIndexPath = tableView.indexPathForSelectedRow { | |
if let coordinator = transitionCoordinator { | |
coordinator.animate(alongsideTransition: { _ in | |
self.tableView.deselectRow(at: selectedIndexPath, animated: true) | |
}, completion: { context in | |
if context.isCancelled { | |
self.tableView.selectRow(at: selectedIndexPath, animated: false, scrollPosition: .none) | |
} | |
}) | |
} else { | |
tableView.deselectRow(at: selectedIndexPath, animated: animated) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for sharing! I've moved the code to an extension on
UITableView
to be able to reuse it easily on anyUIViewController
(as @ogres suggested). Here it is in case that anyone needs it:And the usage from any
UIViewController
:Cheers!