Ping转场动画的实现之二:转场动画

本文探讨如何使用CALayer和mask在iOS中创建自定义转场动画,特别关注在两个UIViewController间的push和pop操作。通过遵循UINavigationControllerDelegate协议,详细展示了实现这一动画效果的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上次说了一下mask,这次来讲一下如何用mask来实现这个动画,关于自定义转场动画的内容可以查看这篇文章,要实现的动画如下:



首页实现两个viewcontroller之间的push和pop,示例代码如下:

第一个viewcontroller

- (void)viewDidLoad {
    [super viewDidLoad];
    //设置背景色
    self.view.backgroundColor = [UIColor greenColor];
    
    //放一张图片
    UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 300, 300)];
    imageView.center = self.view.center;
    imageView.image = [UIImage imageNamed:@"ipad.jpg"];
    [self.view addSubview:imageView];
    
    //创建按钮
    _btn = [UIButton buttonWithType:UIButtonTypeCustom];
    _btn.backgroundColor = [UIColor blackColor];
    _btn.layer.cornerRadius = 22;
    _btn.frame = CGRectMake(self.view.frame.size.width-20-44, 20, 44, 44);
    [_btn addTarget:self action:@selector(goNext) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_btn];
}

//push到下一个
- (void)goNext
{
    SecondViewController *sec = [[SecondViewController alloc]init];
    [self.navigationController pushViewController:sec animated:YES];
}

第二个viewcontroller:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor yellowColor];
    
    UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 300, 300)];
    imageView.center = self.view.center;
    imageView.image = [UIImage imageNamed:@"iphone.jpg"];
    [self.view addSubview:imageView];
    
    _btn = [UIButton buttonWithType:UIButtonTypeCustom];
    _btn.backgroundColor = [UIColor blackColor];
    _btn.layer.cornerRadius = 22;
    _btn.frame = CGRectMake(self.view.frame.size.width-20-44, 20, 44, 44);
    [_btn addTarget:self action:@selector(goBack) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_btn];
}

- (void)goBack
{
    [self.navigationController popViewControllerAnimated:YES];
}

效果如下:



让这两个viewcontroller遵守 <UINavigationControllerDelegate> 协议

@interface ViewController ()<UINavigationControllerDelegate>

@interface SecondViewController ()<UINavigationControllerDelegate>

都设置好代理:

- (void)viewWillAppear:(BOOL)animated
{
    self.navigationController.delegate = self;
}

- (void)viewWillDisappear:(BOOL)animated
{
    self.navigationController.delegate = nil;
}

都实现协议方法(返回一个自定义动画对象):

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
    return myAnimation;
}

接下来就是创建这个自定义的动画对象,创建一个类,遵守  < UIViewControllerAnimatedTransitioning > 协议,并且实现协议方法:

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

@interface CircleAnimation : NSObject <UIViewControllerAnimatedTransitioning>

@end

//.m
#import "CircleAnimation.h"
#import "ViewController.h"
#import "SecondViewController.h"

@interface CircleAnimation ()

@property (assign, nonatomic) id <UIViewControllerContextTransitioning> context;

@end

@implementation CircleAnimation

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

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    //得到上下文
    self.context = transitionContext;
    //获取容器视图
    UIView *container = [transitionContext containerView];
    //获取参与转场的viewcontroller
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    //拿到btn,需要用到他的frame
    UIButton *btn;
    if ([fromVC isKindOfClass:[ViewController class]]) {
        btn = ((ViewController*)fromVC).btn;
    }
    else if ([fromVC isKindOfClass:[SecondViewController class]]) {
        btn = ((SecondViewController*)fromVC).btn;
    }
    
    [container addSubview:toVC.view];
    
    //起始路径
    UIBezierPath *initPath = [UIBezierPath bezierPathWithOvalInRect:btn.frame];
    //大圆半径
    CGFloat newR = sqrt(btn.center.x*btn.center.x + (btn.center.y-CGRectGetHeight(toVC.view.bounds))*(btn.center.y-CGRectGetHeight(toVC.view.bounds)));
    //终点路径
    UIBezierPath *finalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(btn.frame, -newR, -newR)];
    
    //创建一个遮罩
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.path = [finalPath CGPath];
    toVC.view.layer.mask = maskLayer;
    
    //创建动画
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
    animation.fromValue = (__bridge id)([initPath CGPath]);
    animation.toValue = (__bridge id)([finalPath CGPath]);
    animation.duration = [self transitionDuration:transitionContext];
    animation.delegate = self;
    
    //添加动画
    [maskLayer addAnimation:animation forKey:@"path"];
}

//清理工作
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
    [self.context viewControllerForKey:UITransitionContextToViewControllerKey].view.layer.mask = nil;
    [self.context viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil;
    [self.context completeTransition:![self.context transitionWasCancelled]];
}

@end

在两个viewcontroller里创建该动画对象,并在实现的协议方法里,返回它:

- (void)viewDidLoad {
    [super viewDidLoad];
    _ani = [[CircleAnimation alloc]init];
    
    //设置背景色
    ....
}

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
    return _ani;
}

run!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值