【iOS开发】转场动画(一)-简单接触

转场动画:
12.29
自己写过一个转场动画以后,感觉这个转场动画设计的很好,逻辑清晰,自由度也比较大,而且实现起来也比较容易。
一、present 动画
问题:
1、为什么在+ animateTransition方法中,一定要自己调用
[containerview addSubview:toVc.view]
这个有点想不明白,不是应该自己添加的吗??
我在第一次尝试的时候,忘记把toVc.view 添加到上面去,结果虽然最后调用了[transitionContext completeTransition:YES];,但是还是transationView 还在视图中,造成界面不能够再操作。
现在想想,自己添加其实是有了更多的自由度,可以用来管理什么时候再把新的toVc.view 添加到容器上。

自定义转场动画的实现:
1、在要实现自定义转场动画的viewController 的初始化方法里面里面设置转场动画的代理和style:

self.transitioningDelegate = self;
self.modalPresentationStyle = UIModalPresentationCustom;

2、实现转场动画的代理方法。 这里只实现了最基本的两个方法,即present 和dismiss动画。另外还有手势操作和动画被打断时的的代理方法。
这两个代理方法都返回一个遵守<UIViewControllerAnimatedTransitioning>协议的对象,我这里是 CircleTransitionAnimation类。

- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
    return [CircleTransitionAnimation transitioningWithType:CircleTransitionAnimationTypePresent];
}
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    return [CircleTransitionAnimation transitioningWithType:CircleTransitionAnimationTypeDismiss];
}

3、自定义遵守<UIViewControllerAnimatedTransitioning>协议的类,我这里是 CircleTransitionAnimation类。
在这个类中实现相关协议方法来完成转场动画:

#pragma mark UIViewControllerTransitioning
//这个方法返回转场动画的持续时长
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
{
    return 0.5;
}

//这个方法是 转场时要执行的动画,我把代码分到两个私有方法里面了
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
    //animation
    switch (_type) {
        case CircleTransitionAnimationTypePresent:
            [self presentAnimationWithContext:transitionContext];
            break;
        case CircleTransitionAnimationTypeDismiss:
            [self dismissAnimationWithContext:transitionContext];
        default:
            break;
    }
}

// This is a convenience and if implemented will be invoked by the system when the transition context's completeTransition: method is invoked.
- (void)animationEnded:(BOOL) transitionCompleted
{
    NSLog(@"animation ended");
}

#pragma mark CAAnimationDelegate
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
    NSLog(@"animationDidStop");
    switch (_type) {
        case CircleTransitionAnimationTypePresent:{
            NSLog(@"animationDidStop:present");
            id<UIViewControllerContextTransitioning> transitionContext = [anim valueForKey:@"transitionContext"];
            [transitionContext completeTransition:YES];
            NSLog(@"%@",[transitionContext viewControllerForKey:UITransitionContextToViewKey].view);
            //这边为什么取不到 view???
            [transitionContext viewControllerForKey:UITransitionContextToViewKey].view.layer.mask = nil;
            
        }
            break;
        case CircleTransitionAnimationTypeDismiss:{
            NSLog(@"animationDidStop:dismiss");
            id<UIViewControllerContextTransitioning> transitionContext = [anim valueForKey:@"transitionContext"];
            [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
            if ([transitionContext transitionWasCancelled]) {
                [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil;
            }
        }
            break;
    }
}

#pragma mark - custom delegates

#pragma mark - event responses

#pragma mark - public methods 
+ (instancetype)transitioningWithType:(CircleTransitionAnimationType)type
{
    CircleTransitionAnimation *transitioning = [[self alloc]init];
    transitioning.type = type;
    return transitioning;
}

#pragma mark - private methods
- (void)presentAnimationWithContext:(id <UIViewControllerContextTransitioning>)transitionContext
{
    //vc
    ViewController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *containerView = [transitionContext containerView];
    [containerView addSubview:toVc.view];
    //圆形放大动画
    //mask
    UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:fromVc.Btnframe];
    float radius = sqrt(kScreenWidth * kScreenWidth + kScreenHeight * kScreenHeight) / 2.0;
    //没理解
//    CGFloat x = MAX(fromVc.Btnframe.origin.x, containerView.frame.size.width - fromVc.Btnframe.origin.x);
//    CGFloat y = MAX(fromVc.Btnframe.origin.y, containerView.frame.size.height - fromVc.Btnframe.origin.y);
//    CGFloat radius = sqrtf(pow(x, 2) + pow(y, 2));
    UIBezierPath *endPath = [UIBezierPath bezierPathWithArcCenter:containerView.center radius:radius startAngle:0 endAngle:M_PI * 2 clockwise:YES];
    CAShapeLayer *maskLayer = [CAShapeLayer new];
    maskLayer.path = endPath.CGPath;
    maskLayer.fillColor = [UIColor greenColor].CGColor;
    toVc.view.layer.mask = maskLayer;
    //animation
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"path"];
    animation.values = @[(__bridge id)startPath.CGPath,(__bridge id)endPath.CGPath];
    animation.duration = [self transitionDuration:transitionContext];
    animation.keyTimes = @[@1];
    animation.beginTime = CACurrentMediaTime();
    animation.removedOnCompletion = YES;
    animation.delegate = self;
    [animation setValue:transitionContext forKey:@"transitionContext"];
    [maskLayer addAnimation:animation forKey:@"path"];
}

- (void)dismissAnimationWithContext:(id <UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UINavigationController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    ViewController *temp = toVC;
    UIView *containerView = [transitionContext containerView];
    //画两个圆路径
    CGFloat radius = sqrtf(containerView.frame.size.height * containerView.frame.size.height + containerView.frame.size.width * containerView.frame.size.width) / 2;
    UIBezierPath *startCycle = [UIBezierPath bezierPathWithArcCenter:containerView.center radius:radius startAngle:0 endAngle:M_PI * 2 clockwise:YES];
    UIBezierPath *endCycle =  [UIBezierPath bezierPathWithOvalInRect:temp.Btnframe];
    //创建CAShapeLayer进行遮盖
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.fillColor = [UIColor greenColor].CGColor;
    maskLayer.path = endCycle.CGPath;
    fromVC.view.layer.mask = maskLayer;
    //创建路径动画
    CABasicAnimation *maskLayerAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
    maskLayerAnimation.delegate = self;
    maskLayerAnimation.fromValue = (__bridge id)(startCycle.CGPath);
    maskLayerAnimation.toValue = (__bridge id)((endCycle.CGPath));
    maskLayerAnimation.duration = [self transitionDuration:transitionContext];
    maskLayerAnimation.delegate = self;
    maskLayerAnimation.timingFunction = [CAMediaTimingFunction  functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    [maskLayerAnimation setValue:transitionContext forKey:@"transitionContext"];
    [maskLayer addAnimation:maskLayerAnimation forKey:@"path"];
}

这样就完成了一个简单的转场动画,最终的效果图如下:

CircleTransitioning.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容