Doug Smith
Doug Smith

Reputation: 29316

How would I begin to replicate the custom view controller transition Apple uses as the default in iOS 7?

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.)

enter image description here

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: https://dzwonsemrish7.cloudfront.net/items/43260h1u1T1u010i0V3C/DefaultVCTransition.zip

Credit to objc.io for the base code.

Upvotes: 18

Views: 1523

Answers (3)

artud2000
artud2000

Reputation: 544

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+

Upvotes: 0

Dima
Dima

Reputation: 23624

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.

Upvotes: 2

MrHaze
MrHaze

Reputation: 3996

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.

Upvotes: 0

Related Questions