iOS转场动画入门

概述

本文内容是直接从github上找了一个demo,分析了下其源码实现,仅供入门参考,原始链接:https://github.com/YanLYM/YMTransitionDemo

开门动画效果

  • 如图push的时候,当前fromVC从中间往两端逐渐消失,toVC逐渐展示出来;
  • 关闭pop动画正好相反,不分析;


    image.png

实现源码分析

01 AppDelegate代码

  • 没有特殊逻辑,仅仅是用了UINavigationController包裹了一下;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    
    ViewController * viewControllView = [[ViewController alloc] init];
    UINavigationController * na = [[UINavigationController alloc] initWithRootViewController:viewControllView];
    self.window.rootViewController = na;
    
    [self.window makeKeyAndVisible];
    return YES;
}

02 AppDelegate代码

  • YMOpenViewController是一个普通的UIVC子类,唯一的特殊点是实现了协议
YMOpenViewController *vc = [YMOpenViewController new];
self.navigationController.delegate = vc;
[self.navigationController pushViewController:vc animated:YES];

03 YMOpenViewController代码

  • 这里我们只看是如何实现协议中的下面这个方法的,别的都没有实现
  • 从代码可以看到,这个方法需要返回一个实现了协议的对象,self.animation.isPop这个赋值仅仅是为了记录当前是push还是pop。
- (nullable id )navigationController:(UINavigationController *)navigationController
                                            animationControllerForOperation:(UINavigationControllerOperation)operation//None/Push/Pop
                                                         fromViewController:(UIViewController *)fromVC
                                                           toViewController:(UIViewController *)toVC {
    if (operation == UINavigationControllerOperationPush) {
        self.animation.isPop = NO;
    } else if (operation == UINavigationControllerOperationPop) {
        self.animation.isPop = YES;
    }
    return self.animation;
}

- (YMOpenAnimation *)animation {
    if (nil == _animation) {
        _animation = [[YMOpenAnimation alloc] init];
    }
    return _animation;
}

04 YMOpenAnimation代码

  • YMOpenAnimation肯定需要实现协议,上面已经说了。这个协议有两个方法需要实现(其他的先不管),一个是定义动画时长,比较简单;另外一个是定义动画效果是啥,我们看看代码
@protocol UIViewControllerAnimatedTransitioning 

// This is used for percent driven interactive transitions, as well as for
// container controllers that have companion animations that might need to
// synchronize with the main animation.
- (NSTimeInterval)transitionDuration:(nullable id )transitionContext;
// This method can only  be a nop if the transition is interactive and not a percentDriven interactive transition.
- (void)animateTransition:(id )transitionContext;

@optional
...
@end

// 我们只看这一个协议方法是如何实现的
- (void)animateTransition:(id )transitionContext {
    if (!self.isPop) {
        [self pushWithTransition:transitionContext];
    } else {
        [self popWithTransition:transitionContext];
    }
}

// 我们只看push动画(如何开门)
- (void)pushWithTransition:(id )transitionContext {

    // transitionContext其实就类似一个context
    UIView *containerView = [transitionContext containerView];
    UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    
    // 截两个快照用于做动画
    UIView *leftFromView = [fromView snapshotViewAfterScreenUpdates:NO];
    leftFromView.frame = fromView.frame;
    UIView *rightFromView = [fromView snapshotViewAfterScreenUpdates:NO];
    rightFromView.frame = CGRectMake(-fromView.frame.size.width/2, 0, fromView.frame.size.width, fromView.frame.size.height);
    
    // 定义开门动画
    UIView *leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, fromView.frame.size.width/2, fromView.frame.size.height)];
    leftView.clipsToBounds = YES;
    UIView *rightView = [[UIView alloc] initWithFrame:CGRectMake(fromView.frame.size.width/2, 0, fromView.frame.size.width/2, fromView.frame.size.height)];
    rightView.clipsToBounds = YES;
    [leftView addSubview:leftFromView];
    [rightView addSubview:rightFromView];
    [containerView addSubview:toView];
    [containerView addSubview:leftView];
    [containerView addSubview:rightView];
    NSTimeInterval interval = [self transitionDuration:transitionContext];

    // 最终就是一个UIView动画
    [UIView animateWithDuration:interval animations:^{
        leftView.frame = CGRectMake(-fromView.frame.size.width/2, 0, fromView.frame.size.width/2, fromView.frame.size.height);
        rightView.frame = CGRectMake(fromView.frame.size.width, 0, fromView.frame.size.width/2, fromView.frame.size.height);
    } completion:^(BOOL finished) {
        // 最后把额外的view都remove掉再
        BOOL cancel = [transitionContext transitionWasCancelled];
        [transitionContext completeTransition:!cancel];
        if (!cancel) {
            [leftView removeFromSuperview];
            [rightView removeFromSuperview];
        }
    }];
    
}

你可能感兴趣的:(iOS转场动画入门)