iOS自定义转场动画

2023-10-27

如果你在开发中遇到需求,需要实现各种各样的转场动画,那么你可以看看这篇文章。当然,本文并没有实现各种各样的花式转场动画,而是实现了一种思路,抛砖引玉,希望你在看了本文之后能举一反三,随心所欲的定制自己喜欢的转场动画!(注意这里讲的实现仅仅支持iOS7 or later

1345.gif

好了,效果也看到了(好像很丑~~)废话不多说,开撸!

自定义转场动画

我们都知道,苹果在转场动画给了我们自己实现的机会,那么在自己实现的时候就得按着苹果的要求来,也就是遵守相应的协议,下面看看这些协议:

  • UIViewControllerContextTransitioning
  • UIViewControllerAnimatedTransitioning
  • UIViewControllerTransitioningDelegate
  • UINavigationControllerDelegate

UIViewControllerContextTransitioning这个就是一个关于转场上下文对象的协议,它里面提供很多的方法,我们可以通过上下文获取到包括容器视图、要呈现的视图,以及当前的视图(源视图)等诸多有用信息。

UIViewControllerAnimatedTransitioning这个协议里面呢,就是提供了用户自己写转场动画相关的方法:
- (NSTimeInterval)transitionDuration:(nullable id )transitionContext;
这个方法可以提供转场动画的时间

  - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;

这个方法就是主角了,动画核心就在这里面,该方法会去细节实现到底执行什么样的动画,待会再好好分析一下里面的实现。其实想想转场动画实现思路挺简单嘛,就这两个方法。然后再遵守协议(UIViewControllerAnimatedTransitioning)就完了(这仅仅是实现了自定义动画对象~~(姑且叫做动画对象),使用还有其他的步骤)

UIViewControllerTransitioningDelegate 这个协议呢,主要是管理转场动画的, 比如present 和dismiss这种转场,该协议里面的方法就是需要返回一个自定义的遵守了UIViewControllerAnimatedTransitioning协议的对象来作为present或者dismiss的动画对象。那么我们这里主要实现push、pop、present、dismiss这四种动画。那么在实现present 和dismiss这个自定义动画的时候呢就必须要用到这个协议,因为这里面提供了返回present、dismiss动画的动画对象的方法:

      - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;

      - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed

那么我们在这两个方法分别返回之前自定义的不同类型(present、dismiss)的动画对象就可以了。而push、pop这种动画都是要依赖UINavigationController的,那么就得说说UINavigationControllerDelegate协议,该协议里面的方法多数和转场相关,其他还有屏幕方向相关的(这里不讨论)

      - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
      - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

这两个方法都是转场时机方法,有需要可以在这合适的时机干点别的(我可是没干过~~)

      - (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                   animationControllerForOperation:(UINavigationControllerOperation)operation
                                                fromViewController:(UIViewController *)fromVC
                                                  toViewController:(UIViewController *)toVC  NS_AVAILABLE_IOS(7_0);

好了push、pop动画主角上场了,看看该方法的返回值类型就知道之前实现的核心动画对象会成为这里的返回值,看看UINavigationControllerOperation,这是个枚举:

  • UINavigationControllerOperationNone,
  • UINavigationControllerOperationPush,
  • UINavigationControllerOperationPop,

一目了然,那么我们在UINavigationControllerOperationPush的时候就返回‘push’类型的自定义动画对象,在UINavigationControllerOperationPop时就返回‘pop’对象不就可以了,是的,就这么简单,但是好像还有什么不完美的,系统的push、pop是支持边缘手势返回的,我们自定义了动画也得把这个加上啊(追求完美嘛~)苹果也是想到的哦,看看下面这个方法:

    - (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
                                   interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController NS_AVAILABLE_IOS(7_0)
    {
        if(((Animator *)animationController).animationType == AnimationTypeDismiss){
            return self.percentTransition;
        }
        return  nil;
    }

这里先说一下,苹果是为我们准备好了这个类型UIPercentDrivenInteractiveTransition,这个类型的对象是遵守了UIViewControllerInteractiveTransitioning协议的,所以我们只需创建一个UIPercentDrivenInteractiveTransition类型的对象,在这个方法返回就可以了,但是我们还需要自己整一个边缘手势和这个对象关联起来

UIScreenEdgePanGestureRecognizer *pan = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
pan.edges = UIRectEdgeLeft;
[self.view addGestureRecognizer:pan];

- (void)pan:(UIScreenEdgePanGestureRecognizer *)sender
{
    CGFloat progress = [sender translationInView:self.view].x / self.view.frame.size.width;//手指在屏幕滑动的进度百分百

    if (sender.state == UIGestureRecognizerStateBegan) {
        self.percentTransition = [[UIPercentDrivenInteractiveTransition alloc] init]; //开始滑动就初始化这个对象
        [self.navigationController popViewControllerAnimated:YES];
    }else if (sender.state == UIGestureRecognizerStateChanged){
        [self.percentTransition updateInteractiveTransition:progress];
    }else if (sender.state == UIGestureRecognizerStateCancelled || sender.state == UIGestureRecognizerStateEnded){
        if (progress > 0.5) {
            [self.percentTransition finishInteractiveTransition];
        }else{
            [self.percentTransition cancelInteractiveTransition];
        }

        self.percentTransition = nil;
        [sender setTranslation:CGPointZero inView:self.view];
    }
}

这样的话边缘手势返回也搞定了,有木有很简单~ 下面我们再来看看动画核心,这一块就非常的灵活了,动画炫不炫就看这里了,这里的话就提供如图的动画效果:

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        UIView *container  = transitionContext.containerView;

        self.transitionContext = transitionContext;
        //为了兼容iOS7
        UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        self.fromVC = fromVC;
        UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];


        CGPoint startPoint = CGPointMake([UIApplication sharedApplication].keyWindow.center.x, [UIApplication sharedApplication].keyWindow.center.y); //这里用keyWindow和container都可以。感觉应该用container好些~~(尽量用container大小来做参照)

        UIBezierPath *fromPath = [UIBezierPath bezierPathWithArcCenter:startPoint radius:1.0 startAngle:0.0 endAngle:M_PI * 2 clockwise:YES];
    UIBezierPath *toPath = [UIBezierPath bezierPathWithArcCenter:startPoint radius:sqrt([UIScreen mainScreen].bounds.size.width *[UIScreen mainScreen].bounds.size.width + [UIScreen mainScreen].bounds.size.height * [UIScreen mainScreen].bounds.size.height) * 0.5 startAngle:0.0 endAngle:M_PI * 2 clockwise:YES];

        if (self.animationType == AnimationTypePresent) {
    //======================1=====================//
            [container addSubview:toVC.view];

            self.revealAnimation = [CABasicAnimation animationWithKeyPath:@"path"];

            self.shapeLayer.path = toPath.CGPath;

            self.revealAnimation.fromValue = (__bridge id )(fromPath.CGPath);

            self.revealAnimation.toValue = (__bridge id )(toPath.CGPath);

            self.revealAnimation.duration = [self transitionDuration:transitionContext];

            self.revealAnimation.delegate = self;

            [self.shapeLayer addAnimation:self.revealAnimation forKey:@"animation"];

            toVC.view.layer.mask = self.shapeLayer;


    //          [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
    //            toVC.view.frame = container.bounds;
    //        } completion:^(BOOL finished) {
    //            [self.transitionContext completeTransition:finished];  //一定要加上转场结束
    //        }];

        }else if(self.animationType == AnimationTypeDismiss){
    //======================2=====================//
            [container addSubview:toVC.view];

            toVC.view.center = CGPointMake([UIApplication sharedApplication].keyWindow.center.x, [UIApplication sharedApplication].keyWindow.center.y - [UIApplication sharedApplication].keyWindow.bounds.size.height);

            [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
                toVC.view.frame = container.bounds;
            } completion:^(BOOL finished) {
                [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
            }];
        }
     }

    //动画结束
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        if (flag) {
            self.fromVC.view.layer.mask = nil;
            [self.transitionContext completeTransition:flag];
            self.revealAnimation = nil;
            self.shapeLayer = nil;
        }else{
        }
     }

好了,要做自己的动画只需要把“1”、“2”部分替换自己的动画代码就ok了。

结束语

本文动画并不炫,只是将自定义转场动画理了一遍,希望起到抛砖引玉的作用,但愿对你有帮助~。如发现任何知识错误,请下方留言纠正,期待共同进步!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS自定义转场动画 的相关文章

随机推荐

  • ETCD 详解

    一 ETCD简介 etcd是一个Go言编写的分布式 高可用的一致性键值存储系统 用于提供可靠的分布式键值存储 配置共享和服务发现等功能 具有以下特点 简单 易使用 基于HTTP JSON的API让你用curl就可以轻松使用 易部署 使用Go
  • JSON详解

    JSON的全称是 JavaScript Object Notation 意思是JavaScript对象表示法 它是一种基于文本 独立于语言的轻量级数据交换格式 XML也是一种数据交换格式 为什么没有选择XML呢 因为XML虽然可以作为跨平台
  • PC++:矩阵乘法操作

    实验要求 编写矩阵乘法代码实现 并编译执行 对代码进行执行时间分析 比较不同实现的效率差异 实验步骤 1 完成GEMM示例 并修改输入数据大小 首先 建立一个test cpp文件 利用以下代码 来源 https github com pen
  • canvas 画布 arcTo 方法的理解与使用

    参考文档 https codeplayer vip p j7scu arcTo x1 y1 x2 y2 radius x1 y1 理解为端点1 x2 y2 理解为端点2 radius 半径是基于断点2画的一个弧
  • 【uniapp之h5 微信小程序 app 开发项目结构图 思维导图及注意事项】

    uniapp之h5 微信小程序 app 开发项目结构图 思维导图及注意事项
  • python-网络安全编程第三天(正则表达式)

    python 正则表达式 正则表达式本身是一种小型的 高度专业化的编程语言 而在python中 通过内嵌集成re模块 程序媛们可以直接调用来实现正则匹配 正则表达式模式被编译成一系列的字节码 然后由用C编写的匹配引擎执行 用法 match
  • go.DB 富集分析子集 go子集 offspring children多层次结构go

    安装archr包 别处复制 libPaths c home data t040413 R x86 64 pc linux gnu library 4 2 home data t040413 R yll usr local lib R sit
  • Interceptor的基本介绍和使用preHandle、postHandle与afterCompletion

    目录 preHandle postHandle afterCompletion 项目测试代码 项目测试 preHandle 调用时间 Controller方法处理之前 执行顺序 链式Intercepter情况下 Intercepter按照声
  • 通用单目标跟踪综述《Handcrafted and Deep Trackers: A Review of Recent Object Tracking Approaches》

    近年来 视觉目标跟踪成为一个非常活跃的研究领域 每年都会提出越来越多的跟踪算法 跟踪在人机交互 自动驾驶汽车 机器人 监控和安全等各种现实问题中有着广泛的应用 本文将回顾跟踪领域的最新的趋势和进展 并基于特征提取方法评估了不同跟踪算法的鲁棒
  • pickle读文件解码问题

    运行 Revisiting Semi Supervised Learning with Graph Embeddings 的代码 kimiyoung planetoid 其中用 pickle 读数据文件出现问题 它本身是用 python 2
  • Nacos修改配置文件如何使其立即生效

    目录 前言 实现方案 方案一 方案二 注意事项 前言 当配置信息发生变动时 修改实时生效 无需要重新重启服务 就能够自动感知相应的变化 并将新的变化统一发送到相应程序上 快速响应变化 要实现这一目的 需要通过下面两种方案来实现 实现方案 方
  • 渗透测试工具ZAP入门教程(3)-渗透测试扫描流程

    使用ZAP对网站进行渗透测试流程如下 ZAP启动浏览器 输入URL 点击启动浏览器 在打开的浏览器登录要扫描的网站 在打开的浏览器上进行需要测试的流程操作 ZAP会记录操作过程中的HTTP请求 spider爬虫 点击Spider Start
  • 永恒话题,编程语言的选择

    当编程初学者要选择编程语言时 可以像选择一家餐厅一样 下面是一些建议 像选择美食一样选择编程语言 想象你是个美食家 编程语言就是菜单上的各种美食 先思考你的目标和口味偏好 是想开发网页 那就选择前端语言如HTML CSS和JavaScrip
  • 哈希(Hash)和哈希树(Merkle tree)

    哈希函数 英语 Hash function 又称散列函数 是一种从任何一种数据中创建小的数字 指纹 的方法 散列函数把消息或数据压缩成摘要 使得数据量变小 将数据的格式固定下来 该函数将数据打乱混合 重新创建一个叫做散列值 哈希值 hash
  • 白菜板裸机程序转换成智龙板PMON引导程序

    裸机程序转换成PMON引导程序 根据勤为本 gitee 上的龙芯1C 裸机程序 应用于白菜板 修改 Makefile 和 ld script 和 start s 使之应用于智龙开发板 1 替换修改文件 1 1 make与ld script
  • python 编码规范 Style Guide for Python Code

    目录 python 编码规范简述 编码规范的好处 PEP Python Enhancement Proposals 简介 规范基本内容 代码的整体布局 缩进与空格 制表符 隐式换行 悬挂缩进 行最大长度 运算符与换行 代码之间的空行 导入的
  • vue、uniapp调试工具【vueDevTools】

    点击下载
  • 【GD32篇】CAN总线入门教程——实现数据收发

    本文主要介绍CAN总线的软件配置 1 简介 CAN 总线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线 并且拥有以 CAN 为底层协议专为大型货车和重工机械车辆设计的 J1939 协议 近年来 它具有的高可靠性和良好的错误检
  • 算法与数据结构可视化网站

    今天画B 树用到了 算法与数据结构可视化网站 https www cs usfca edu galles visualization Algorithms html
  • iOS自定义转场动画

    如果你在开发中遇到需求 需要实现各种各样的转场动画 那么你可以看看这篇文章 当然 本文并没有实现各种各样的花式转场动画 而是实现了一种思路 抛砖引玉 希望你在看了本文之后能举一反三 随心所欲的定制自己喜欢的转场动画 注意这里讲的实现仅仅支持