I'm trying to get a handle on new iOS 7 APIs that allow for interactive, animated view controller transitions, including transitions between UICollectionViewLayout
s.
I've taken and modified sample code from WWDC 2013, "iOS-CollectionViewTransition", which can be found here: https://github.com/timarnold/UICollectionView-Transition-Demo
The original demo, which was not in a working state when I found it, can be accessed with an Apple Developer account, here: https://developer.apple.com/downloads/index.action?name=WWDC%202013
My version of the app presents a collection view with two layouts, both UICollectionViewFlowLayout
layouts with different properties.
Tapping on a cell in the first layout properly animates to the second, including, crucially, the tapped-on-item being scrolled to in the new layout. At first I was confused about how the new collection view knows to set its content offset so that the appropriate cell is visible, but I learned it does this based on the selected
property of the presenting collection view.
Pinching on an item in the first layout should animate, using UICollectionViewTransitionLayout
, UIViewControllerAnimatedTransitioning
, and UIViewControllerInteractiveTransitioning
, to the new layout as well. This works, but the pinched-at cell is not scrolled to in the new layout or the transition layout.
I've tried setting the selected
property on the pinched-on cell at various locations (to try to mimic the behavior described when tapping on an item to push the new view controller), to no avail.
Any ideas about how to solve this problem?
You can manipulate the contentOffset
yourself during the transition, which actually gives you finer-grained control than UICollectionView's
built-in animation.
For example, you can define your transition layout like this to interpolate between the "to" and "from" offsets. You just need to calculate the "to" offset yourself based on how you want things to end up:
@interface MyTransitionLayout : UICollectionViewTransitionLayout
@property (nonatomic) CGPoint fromContentOffset;
@property (nonatomic) CGPoint toContentOffset;
@end
#import "MyTransitionLayout.h"
@implementation MyTransitionLayout
- (void) setTransitionProgress:(CGFloat)transitionProgress
{
super.transitionProgress = transitionProgress;
CGFloat f = 1 - transitionProgress;
CGFloat t = transitionProgress;
CGPoint offset = CGPointMake(f * self.fromContentOffset.x + t * self.toContentOffset.x, f * self.fromContentOffset.y + t * self.toContentOffset.y);
self.collectionView.contentOffset = offset;
}
@end
One thing to note is that the contentOffset
will be reset to the "from" value when the transition completes, but you can negate that by setting it back to the "to" offset in the completion block of startInteractiveTransitionToCollectionViewLayout
CGPoint toContentOffset = ...;
[self.collectionViewController.collectionView startInteractiveTransitionToCollectionViewLayout:layout completion:^(BOOL completed, BOOL finish) {
if (finish) {
self.collectionView.contentOffset = toContentOffset;
}
}];
UPDATE
I posted an implementation of this and a working example in a new GitHub library TLLayoutTransitioning. The example is non-interactive, intended to demonstrate improved animation over setCollectionViewLayout:animated:completion
, but it utilizes the interactive transitioning APIs combined with the technique described above. Take a look at the TLTransitionLayout
class and try running the "Resize" example in the Examples workspace.
Perhaps TLTransitionLayout
can accomplish what you need.
UPDATE 2
I added an interactive example to the TLLayoutTransitioning library. Try running the "Pinch" example in the Examples workspace. This one pinches the visible cells as a group. I'm working on another example that pinches an individual cell such that the cell follows your fingers during the transition while the other cells follow the default linear path.
UPDATE 3
I've recently added more content offset placement options: Minimal, Center, Top, Left, Bottom and Right. And transitionToCollectionViewLayout:
now supports 30+ easing functions courtesy of Warren Moore's AHEasing library.
Thank you Timothy Moose. It works for iOS14 too. I didn't try interactions via finger but for a simple animation of changing a grid layout on list layout it works fine. You can replace
self.collectionView?.contentOffset = ...
with
setContentOffset(_ contentOffset: yourOffset, animated: false)
If you don't do this, content will bounce a bit during the animation.
Here's my example in Swift:
final class SFDocumentsManagerTransitionLayout: UICollectionViewTransitionLayout {
var fromContentOffset: CGFloat = 0
var toContentOffset: CGFloat = 0
override var transitionProgress: CGFloat {
didSet {
let f = 1 - self.transitionProgress
let t = self.transitionProgress
self.collectionView?.setContentOffset(CGPoint(x: 0,
y: f * self.fromContentOffset + t * self.toContentOffset),
animated: false)
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With