欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

iOS核心动画CoreAnimation系统进阶(自定义转场动画)

程序员文章站 2022-04-11 13:19:30
...

iOS核心动画CoreAnimation系统进阶(自定义转场动画)

想要实现自定义转场动画,需要分两个步骤

一.实现相关协议

@interface CustomTransitionViewController ()<UINavigationControllerDelegate>
-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    self.navigationController.delegate = self;
}
//告诉nav,想自己自定义一个转场
-(id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
    if (operation == UINavigationControllerOperationPush) {
        WTCircleTransition *trans =[WTCircleTransition new];

        return trans;
    }
    return nil;
}

二.做动画

创建WTCircleTransition协议类

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface WTCircleTransition : NSObject<UIViewControllerAnimatedTransitioning>

@end

实现UIViewControllerAnimatedTransitioning中的协议

iOS核心动画CoreAnimation系统进阶(自定义转场动画)

WTCircleTransition.m

-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
    return .8f;
}
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
    //1.持有transitionContext上下文
    _context = transitionContext;
    //2.获取view的容器
    UIView *containerView = [transitionContext containerView];
    //3.初始化toVc,把toVc的view添加到容器view
    UIViewController *toVc =[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    //4.添加动画
    /*
     拆分动画
     4.1 2个圆(大小圆的中心点一致)
     4.2 贝塞尔
     4.3 蒙版
     */
    UIButton *btn;
    CustomTransitionViewController * VC1;
    SecondViewController * VC2;
    if (_isPush) {
        VC1 =[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        VC2 = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        btn = VC1.customTransitionBtn;
    }else{
        VC2 =[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        VC1 = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        btn = VC2.backBtn;
    }
     [containerView addSubview:VC1.view];
     [containerView addSubview:VC2.view];
    //5.画出小圆
    UIBezierPath *smallPath =[UIBezierPath bezierPathWithOvalInRect:btn.frame];//内切圆
    CGPoint centerP;
    centerP = btn.center;
    //6.求大圆半径 勾股定理
    CGFloat radius;
    CGFloat y = CGRectGetHeight(toVc.view.bounds)-CGRectGetMaxY(btn.frame)+CGRectGetHeight(btn.bounds)/2;
    CGFloat x = CGRectGetWidth(toVc.view.bounds)-CGRectGetMaxX(btn.frame)+CGRectGetWidth(btn.bounds)/2;
    if (btn.frame.origin.x >CGRectGetWidth(toVc.view.bounds)/2) {
        //位于14象限
        if (CGRectGetMaxY(btn.frame)< CGRectGetHeight(toVc.view.bounds)/2) {
            //第一象限
            //sqrtf(求平方根)
            radius = sqrtf(btn.center.x *btn.center.x +y*y);
        }else{
            //第四象限
            radius = sqrtf(btn.center.x * btn.center.x + btn.center.y*btn.center.y);
        }
    }else{
        if (CGRectGetMaxY(btn.frame)<CGRectGetHeight(toVc.view.frame)) {
            //第二象限
            radius = sqrtf(x*x+y*y);
        }else{
            //第三象限
            radius = sqrtf(x*x + btn.center.y*btn.center.y);
        }
    }
    //7.画大圆
    UIBezierPath * bigPath = [UIBezierPath bezierPathWithArcCenter:centerP radius:radius startAngle:0 endAngle:2*M_PI clockwise:YES];
    //8.
    CAShapeLayer *shapeLayer =[CAShapeLayer layer];

    if (_isPush) {
        shapeLayer.path = bigPath.CGPath;
    } else{
        shapeLayer.path = smallPath.CGPath;
    }

    //9.添加蒙版
//    [toVc.view.layer addSublayer:shapeLayer];
    UIViewController *VC;
    if (_isPush) {
        VC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    }else{
        VC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    }
    VC.view.layer.mask = shapeLayer;

    //10.给layer添加动画
    CABasicAnimation *anim =[CABasicAnimation animationWithKeyPath:@"path"];
    if (_isPush) {
         anim.fromValue = (id)smallPath.CGPath;
    }else{
         anim.fromValue = (id)bigPath.CGPath;
    }
    //重要,动画时间要和转场时间一致
    anim.duration = [self transitionDuration:transitionContext];
    anim.delegate = self;
    [shapeLayer addAnimation:anim forKey:nil];
}
#pragma mark - CAAnimationDelegate
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
    [_context completeTransition:YES];
    //去掉蒙版
    if (_isPush) {
        UIViewController * toVc =[_context viewControllerForKey:UITransitionContextToViewControllerKey];
        toVc.view.layer.mask = nil;
    }else{
        UIViewController * toVc =[_context viewControllerForKey:UITransitionContextFromViewControllerKey];
        toVc.view.layer.mask = nil;
    }

}

iOS核心动画CoreAnimation系统进阶(自定义转场动画)

大圆半径

iOS核心动画CoreAnimation系统进阶(自定义转场动画)

勾股定理求半径

iOS核心动画CoreAnimation系统进阶(自定义转场动画)

判断象限

在做自定义转场动画时,我们需要进行几个步骤:

  1. 实现相关协议 push/pop motal segue
  2. 添加实现了UIViewControllerAnimatedTransitioning的类
  3. 在类里面实现动画协议,然后添加转场动画
  4. 告诉上下文,动画完成

总结:自定义切换并不会改变VC的组织结构,只是负责提供了view的效果

实现效果:

iOS核心动画CoreAnimation系统进阶(自定义转场动画)

gitHub代码参考