2
votes

The initial ViewController of my app has 2 container views: The first contains a ViewController inside which is a UINavigationController which controls the main content of the app. The second container view of the initial VC is a small strip with UILabels positioned directly below where the the Navigation Bar will be.

The initial VC of the navigation stack of the main content VC has the Navigation Bar hidden. All subsequent VC's will display the Navigation Bar along with the second container view of the initial VC. I would like to animate in the second container VC when the app navigates away from the initial VC of the navigation stack.

My question is, what animation do I need to create to match the animation a Navigation Controller performs when it pushes/pops VC on/off the navigation stack? I'm currently using:

[UIView animateWithDuration:0.27f delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
    self.playerCardVC.frame = secondFrame;
} completion:nil];

But it's not matching up exactly when the Navigation Bar animates into view. Any thoughts/ideas would be greatly appreciated.

UPDATE:

I've been searching online for an answer to this issue along with trying to tweak the animation posted in my initial question but cannot seem to perfectly match the navigation bar as it animates into view. Any comments or points in the right direction will be truly appreciated.

2

2 Answers

0
votes

It's very tough to understand your view hierarchy and navigation design from the description above, perhaps you can post some screenshots or sketches?

You can override the standard horizontal push/pop animations of a UINavigationController. You do so by defining a custom UINavigationControllerDelegate object and a few other things. See below.

Setup your navController and navControllerDelegate like so:

UINavigationController *navigationController = [[UINavigationController alloc] init];
self.navigationControllerDelegate = [[NavigationControllerDelegate alloc] init];
navigationController.delegate = self.navigationControllerDelegate;

Where the NavigationControllerDelegate class looks like this:

@interface NavigationControllerDelegate : NSObject <UINavigationControllerDelegate>

@end


#import "NavigationControllerDelegate.h"
#import "CrossFadePushAnimator.h"
#import "CrossFadePopAnimator.h"

@interface NavigationControllerDelegate ()

@property (nonatomic, strong) CrossFadePushAnimator* pushAnimator;
@property (nonatomic, strong) CrossFadePopAnimator* popAnimator;

@end

@implementation NavigationControllerDelegate

- (id)init
{
    self = [super init];
    if (self)
    {
        self.pushAnimator = [CrossFadePushAnimator new];
        self.popAnimator = [CrossFadePopAnimator new];
    }

    return self;
}

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
    {
    if (operation == UINavigationControllerOperationPop)
    {
        return self.popAnimator;
    }
    else if (operation == UINavigationControllerOperationPush)
    {
        return self.pushAnimator;
    }

    return nil;
}

- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController
{
    return nil;
}

The pushAnimator looks something like this:

@interface CrossFadePushAnimator ()

@property (nonatomic, strong) id<UIViewControllerContextTransitioning> transitionContext;

@end

@implementation CrossFadePushAnimator

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
    return AnimationDuration;
}

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//    UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

    [[transitionContext containerView] addSubview:toViewController.view];
    toViewController.view.alpha = 1.0f;

    // We are using CAAnimation instead of UIView animation because we need the UIToolbar blur layer to animate
    CABasicAnimation *fade = [CABasicAnimation animationWithKeyPath:@"opacity"];
    fade.fromValue = @0;
    fade.toValue = @1;
    fade.duration = [self transitionDuration:transitionContext];
    fade.removedOnCompletion = YES;
    fade.delegate = self;

    self.transitionContext = transitionContext;
    [toViewController.view.layer addAnimation:fade forKey:AnimationKey];
}

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
    [self.transitionContext completeTransition:![self.transitionContext transitionWasCancelled]];
    self.transitionContext = nil;
}

And the popAnimator looks something like the pushAnimator.

Hope this helps!

0
votes

It's very tough to understand your view hierarchy and navigation design from the description above, perhaps you can post some screenshots or sketches?

You can override the standard horizontal push/pop animations of a UINavigationController. You do so by defining a custom UINavigationControllerDelegate object and a few other things. See below.

Setup your navController and navControllerDelegate like so:

UINavigationController *navigationController = [[UINavigationController alloc] init];
self.navigationControllerDelegate = [[NavigationControllerDelegate alloc] init];
navigationController.delegate = self.navigationControllerDelegate;

Where the NavigationControllerDelegate class looks like this:

@interface NavigationControllerDelegate : NSObject <UINavigationControllerDelegate>

@end


#import "NavigationControllerDelegate.h"
#import "CrossFadePushAnimator.h"
#import "CrossFadePopAnimator.h"

@interface NavigationControllerDelegate ()

@property (nonatomic, strong) CrossFadePushAnimator* pushAnimator;
@property (nonatomic, strong) CrossFadePopAnimator* popAnimator;

@end

@implementation NavigationControllerDelegate

- (id)init
{
    self = [super init];
    if (self)
    {
        self.pushAnimator = [CrossFadePushAnimator new];
        self.popAnimator = [CrossFadePopAnimator new];
    }

    return self;
}

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
    {
    if (operation == UINavigationControllerOperationPop)
    {
        return self.popAnimator;
    }
    else if (operation == UINavigationControllerOperationPush)
    {
        return self.pushAnimator;
    }

    return nil;
}

- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController
{
    return nil;
}

The pushAnimator looks something like this:

@interface CrossFadePushAnimator ()

@property (nonatomic, strong) id<UIViewControllerContextTransitioning> transitionContext;

@end

@implementation CrossFadePushAnimator

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
    return AnimationDuration;
}

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    [[transitionContext containerView] addSubview:toViewController.view];
    toViewController.view.alpha = 1.0f;

    // I'm using CABasicAnimation here for a specific reason, but you could also use the animation method you use above.
    CABasicAnimation *fade = [CABasicAnimation animationWithKeyPath:@"opacity"];
    fade.fromValue = @0;
    fade.toValue = @1;
    fade.duration = [self transitionDuration:transitionContext];
    fade.removedOnCompletion = YES;
    fade.delegate = self;

    self.transitionContext = transitionContext;
    [toViewController.view.layer addAnimation:fade forKey:AnimationKey];
}

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
    [self.transitionContext completeTransition:![self.transitionContext transitionWasCancelled]];
    self.transitionContext = nil;
}

And the popAnimator looks something like the pushAnimator.

Hope this helps!