Using iOS 7's custom view controller transitions, I want to achieve a visual effect similar to Apple's default view controller transition in iOS 7.
(The one where you can slide to pop a view controller off the stack by sliding it from the left to the right, where the top view controller slides off the top of the other with a shadow and the navigation bar shifts.)
I'm having a great deal of difficulty implementing this, though. Most of the tutorials on custom view controllers go with very different effects than the default in order to show what the API is capable off, but I want to replicate this one.
In my subclass for implementing <UIViewControllerAnimatedTransitioning>
I have the following code for the interactive animation:
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[transitionContext.containerView addSubview:toViewController.view];
[transitionContext.containerView addSubview:fromViewController.view];
fromViewController.view.layer.shadowOffset = CGSizeMake(0.0, 0.0);
fromViewController.view.layer.shadowColor = [UIColor blackColor].CGColor;
fromViewController.view.layer.shadowRadius = 5.0;
fromViewController.view.layer.shadowOpacity = 0.5;
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
CGRect newFrame = fromViewController.view.frame;
newFrame.origin.x = CGRectGetWidth(fromViewController.view.bounds);
fromViewController.view.frame = newFrame;
} completion:^(BOOL finished) {
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
However the shadow code makes it lag tremendously (even if I use the new snapshot
methods) and I cannot figure out how to manipulate the navigation bar at all.
Has anyone tried to do something similar to this and are able to provide sample code?
Sample project for testing if you'd like: http://cl.ly/0B3q1b390x0D
Credit to objc.io for the base code.
Setting the shadowPath
greatly increases the performance of this shadow.
Just add this in your animateTransition:
method after you have set the shadow properties. This avoids the expensive offscreen rendering that shadow normally causes.
[fromViewController.view.layer setShadowPath:[[UIBezierPath bezierPathWithRect:fromViewController.view.bounds] CGPath]];
I downloaded your sample project and did that, the stutter is now completely gone.
Some info on what this does here.
EDIT:
The answer about manipulating the navigation bar animation is that it doesn't seem like you can. In order to do so you will need to reimplement your own NavigationController-type class from scratch. The transition animation for the navigation bar is done internally by the container view controller (the UINavigationController
) and is not surfaced anywhere in the code.
Have a look at ECSlidingViewController I've used it a couple of times, if you understand how it works, you can easily implement something similar.
If you want to reinvent the wheel, my answer is no use, sorry.
I create the effect using Dynamics I create my own ViewController subclass with the next methods.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil forMultipleDirections:(BOOL)isTwoDirections;
- (void)addVelocityForGesture:(UIPanGestureRecognizer *)recognizer;
- (UIDynamicItemBehavior*) itemBehaviourForView;
Then I added physics and a gesture recognizer in order to handle the paning gesture. to make it look in top just add a shadow offset.
_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.parentViewController.view];
_gravity = [[UIGravityBehavior alloc] init];
_gravity.gravityDirection = CGVectorMake(-1.5, 0);
[_animator addBehavior:_gravity];
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.view]];
[collision addBoundaryWithIdentifier:@1 fromPoint:CGPointMake(0, 0) toPoint:CGPointMake(0, self.parentViewController.view.frame.size.height)];
[_animator addBehavior:collision];
[_gravity addItem:self.view];
UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.view]];
[itemBehavior setElasticity:0.2];
[_animator addBehavior:itemBehavior];
You will need more methods to add velocity according to the direction of the panning, then you will need to find a point where you want to dismiss or hide the childViewController depending in your use case, etc... I did it and it works pretty fine for me as it's using Dynamics it just supports iOS7+