iOS_Custom Transition Animation 自定义转场动画

news2025/1/21 10:21:28

文章目录

  • 1、push-pop 动画协议
  • 2、present-dismiss 动画协议
  • 3、实现转场动画协议
    • 3.1 动画时长
    • 3.2 push or present animation (显示动画)
    • 3.3 动画结束
    • 3.4 pop or dismiss animation (消失动画)
  • 4、UIPresentationController
    • 4.1 设置presentVC的frame
    • 4.2 present 动画
    • 4.3 dismiss 动画


1、push-pop 动画协议

想要在 pushpop viewController 时使用自定义的转场动效,需要设置self.naviagtionController.delegate, 并实现UINavigationControllerDelegate的一个方法:

// 返回一个实现了转场动画协议的对象
func navigationController(_ navigationController: UINavigationController,
                            animationControllerFor operation: UINavigationController.Operation,
                            from fromVC: UIViewController,
                            to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    if operation == .push {
        return a push animator // 实现push动画的对象
    } 
    if operation == .pop {
        return a pop animator // 实现pop动画的对象
    } 
}

2、present-dismiss 动画协议

想要在 presentdismiss viewController 时使用自定义的转场动效,需要设置toViewController.transitioningDelegate, 并实现UIViewControllerTransitioningDelegate协议的两个方法:

// 返回一个实现了 present 转场动画协议的对象
func animationController(forPresented presented: UIViewController,
                         presenting: UIViewController,
                         source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return a present animator // 实现 present 动画的对象
}
// 返回一个实现了 dismiss 转场动画协议的对象
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return a pop animator // 实现 dismiss 动画的对象
}

Tips: 这个协议谁实现都可以:fromVC or toVC or new an object, as you like.


3、实现转场动画协议

以上2个协议返回的4个animator都是实现了UIViewControllerAnimatedTransitioning协议的对象。举例实现如下:

push+present-animation

3.1 动画时长

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    return 0.5  // 返回动画时长
}

3.2 push or present animation (显示动画)

执行动画的方法animateTransition,带了一个遵循UIViewControllerContextTransitioning协议的transitionContext参数。具体可以取到哪些数据详情可见UIViewControllerContextTransitioning。
以下列举一些常用的:

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    // 1. get data for animation (获取动画需要的数据)
    // animation contianer (动画容器)
    let containerView = transitionContext.containerView
    
    // come from viewController (来源页面的)
    // viewController
    let fromVC = transitionContext.viewController(forKey: .from)
    // view
    let fromView = transitionContext.view(forKey: .from)
    // 初始frame
    let fromViewInitialFrame = transitionContext.initialFrame(for: fromVC)
    // 最终frame
    let fromViewFinalFrame = transitionContext.finalFrame(for: fromVC)
    
    // to viewController (跳转页面的)
    // viewController
    let toVC = transitionContext.viewController(forKey: .to)
    // view
    let toView = transitionContext.view(forKey: .to)
    // 初始frame
    var toViewInitialFrame = transitionContext.initialFrame(for: toVC)
    // 最终frame
    let toViewFinalFrame = transitionContext.finalFrame(for: toVC)
    
    // and so on ...... 
    // do animation with available data (根据拿到的数据做动画)
    
    // 2. calculate the value what you want (计算初始位置+最终位置)
    toViewInitialFrame.origin.x = containerFrame.size.width;
    toViewInitialFrame.origin.y = containerFrame.size.height;
    
    // 3. Add do toView to the contenerView, and set the initial value (添加 toView 到 contianerView 上, 并设置初始值)
    containerView.addSubview(toView)
    toView.frame = toViewInitialFrame;
    
    // Add additional views required for animation and set initial values
    // 添加动画所需的其他视图并设置初始值
    ...... 

    // 4. execute animation 执行动画
    UIView.animate(withDuration: self.transitionDuration(using: transitionContext)) {
        // 5.1 set final frame for animation view
        toView.frame = toViewFinalFrame
        
        // Set additional views final values
        ......
    
    } completion: { finish in
        // 5.2 get animation result
        let success = !transitionContext.transitionWasCancelled
        // 5.2.1 remove the view if animation fail
        if !success {
            toView.removeFromSuperview()
        }
        // 5.2.1 callback animation result
        transitionContext.completeTransition(success)
    }
}

3.3 动画结束

动画结束方法:

func animationEnded(_ transitionCompleted: Bool) {
    // transitionCompleted 动画执行结果: YES-success NO-fail
}

3.4 pop or dismiss animation (消失动画)

大致跟显示动画一致,转场动画都是需要显示toView, 让fromView消失

  1. 上面显示动画的例子:
    是将toView加到containerView上,并对齐进行动画。
  • 动画成功:toView就显示在conatinerView上,进入的是下一个页面
  • 动画失败即转场失败:则应该将toView从容器上移除,即还停留在原来的页面上。
  1. 以下消失动画的例子:
    同样是将toView加到containerView上,但用的是fromView进行动画。
  • 动画成功:应将fromView从容器上移除,进入下一个页面
  • 动画失败即转场失败:则不会移除fromView,即还停留在原来的页面上。
/// 转场动画
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    // 1. get data for animation (获取动画需要的数据)
    ......
    let toViewStartFrame = transitionContext.initialFrame(for: toVC)
    var fromViewFinalFrame = transitionContext.finalFrame(for: fromVC)
    
    // 2. calculate the value what you want (计算初始位置+最终位置)
    fromViewFinalFrame = CGRect(x: CGRectGetWidth(containerFrame),
                                y: CGRectGetHeight(containerFrame),
                                width: CGRectGetWidth(fromView.frame),
                                height: CGRectGetHeight(fromView.frame))
    
    // 3. Add do toView to the contenerView, and set the initial value (添加 toView 到 contianerView 上, 并设置初始值)
    containerView.addSubview(toView)
    toView.frame = toViewStartFrame;
    
    // 4. execute animation 执行动画
    UIView.animate(withDuration: self.transitionDuration(using: transitionContext)) {
        // 5.1 set final frame for animation view
        fromView.frame = fromViewFinalFrame

    } completion: { finish in
        // 5.2 get animation result
        let success = !transitionContext.transitionWasCancelled
        // 5.2.1 remove the view after animation finish
        if success {
            fromView.removeFromSuperview()
        }
        transitionContext.completeTransition(success)
    }
}

消失动画里需要注意的是,如果是pop是能拿到toView,但如果是dimiss是拿不到toView的。


4、UIPresentationController

presentdismiss动画如果想在一个中间的viewController进行,则在实现UIViewControllerTransitioningDelegate协议时,不要实现以上2个返回animator的方法,而是实现以下返回UIPresentationController的方法:

// 返回实现 present-dismiss 动效的VC
func presentationController(forPresented presented: UIViewController,
                            presenting: UIViewController?,
                            source: UIViewController) -> UIPresentationController? {
    return a presentation controller // 实现 present-dismiss 动画的对象
}

官方这个例子主要的动画是设置presentVCframeframeOfPresentedViewInContainerViewpresentfinalframe,是dismissinitialFrame
然后在presentationTransitionWillBegindismissalTransitionWillBegin方法里执行的动画,仅是添加了一个偏暗的背景View,然后调整alpha动画显示消失

效果如如下:
presentController


4.1 设置presentVC的frame

// presentVC 在动画容器上的 frame
override var frameOfPresentedViewInContainerView: CGRect {
    get {                    
        let containerBounds: CGRect = self.containerView?.bounds ?? .zero
        let width = CGFloat(floorf(Float(containerBounds.size.width) / 2.0))
        let height = containerBounds.size.height
        let originX = containerBounds.size.width - width
                           
        return CGRect(x: originX, y: 0.0, width: width, height: height)
    }
}

4.2 present 动画

// 暗色背景
lazy var moDimmingView: UIView = {
    let view = UIView(frame: .zero)
    view.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
    view.alpha = 0.0
    return view
}()
// MARK: - 将要开始 present,设置初始值 和 动画回调
override func presentationTransitionWillBegin() {
    super.presentationTransitionWillBegin()    
    // 1. get animation container view (获取动画容器视图)
    guard let containerView = containerView else { return }
    
    // 2. set initial value for animation views and add to container view (设置动画视图的初始值, 并添加到都到容器上)
    self.moDimmingView.frame = containerView.bounds
    self.moDimmingView.alpha = 0.0
    containerView.insertSubview(self.moDimmingView, at: 0)
    
    // 3. execute animation (执行动画)
    // 这里尝试去拿一个时间点的回调,能拿到就在回调里执行显示动画;拿不到就直接设置显示
    guard let transitionCoordinator = self.presentedViewController.transitionCoordinator else {
        self.moDimmingView.alpha = 1.0
        return
    }
    transitionCoordinator.animateAlongsideTransition(in: self.presentedView) { context in
        self.moDimmingView.alpha = 1.0
    }
}

// MARK: - present 动画结束
override func presentationTransitionDidEnd(_ completed: Bool) {
    super.presentationTransitionDidEnd(completed)    
    // remove dark background view when transition fail
    if !completed {
        self.moDimmingView.removeFromSuperview()
    }
}

4.3 dismiss 动画

// MARK: - 将要开始 dismiss,设置初始值 和 动画回调
override func dismissalTransitionWillBegin() {
    super.dismissalTransitionWillBegin()    
    guard let transitionCoordinator = self.presentedViewController.transitionCoordinator else {
        self.moDimmingView.alpha = 0.0
        return
    }
    transitionCoordinator.animateAlongsideTransition(in: self.presentedView) { context in
        self.moDimmingView.alpha = 0.0
    }
}

// MARK: - dismiss 动画结束
override func dismissalTransitionDidEnd(_ completed: Bool) {
    super.dismissalTransitionDidEnd(completed)    
    if completed {
        self.moDimmingView.removeFromSuperview()
    }
}

以上,参照官方的例子,可以根据需要写出想要的动画


Demo:github address


参考:
Customizing the Transition Animations
Creating Custom Presentations

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/40727.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker快速安装Oracle 12c

【Oracle系列3】Docker快速安装Oracle 12c 背景 现在还很多企业用12c,以这个版本为例,介绍docker快速启动Oracle并做实验 步骤 1、docker环境的安装(略) 2、查询镜像,挑选镜像 docker search oracle结果 StoneM…

阿里P8架构师都在学习参考的SpringCloud微服务实战文档

我一直在使用Spring Boot、Spring Data等框架来进行开发工作。 作为一名Spring系列的忠实粉丝,我自然希望能够有更多的开发者参与进来,于是自己坚持写Spring Cloud相关的文章,并且将文章涉及的代码整理后放在GitHub上分享。 这使我得到了很…

【Hack The Box】Linux练习-- Luanne

HTB 学习笔记 【Hack The Box】Linux练习-- Luanne 🔥系列专栏:Hack The Box 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 📆首发时间:🌴2022年11月24日🌴 &#x1f3…

零基础搭建基于知识图谱的电影问答系统

零基础搭建基于知识图谱的电影问答系统一、项目准备二、项目数据三、训练问题分类器四、准备问答模板五、搭建webapp六、问题预处理一、项目准备 首先需要一款python编译器,本人选用的是PyCharm,搭建好Python环境;安装第三方依赖库&#xff…

【Hack The Box】linux练习-- Delivery

HTB 学习笔记 【Hack The Box】linux练习-- Delivery 🔥系列专栏:Hack The Box 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 📆首发时间:🌴2022年11月17日🌴 &#x1…

黄佳《零基础学机器学习》chap1笔记

黄佳 《零基础学机器学习》 chap1笔记 这本书实在是让我眼前一亮!!! 感觉写的真的太棒了! 文章目录黄佳 《零基础学机器学习》 chap1笔记第1课 机器学习快速上手路径—— 唯有实战1.1 机器学习族谱1.2 云环境入门实践:…

ERD Online 4.0.4 元数据在线建模(免费、私有部署)

❝ fix(erd): 修改表名、模块名自定义提示fix(erd): 支持自定义表名显示格式fix(erd): 升级ant到5.0.1版本fix(erd): 修复PDMan导入类型列为空fix(erd): 增加类型列宽度,避免类型显示不全fix(erd): 修复表设计报undefine异常fix(erd): 修复版本比对,出现…

二分搜索算法框架解析

文章目录 一、寻找一个数(基本的二分搜索)二、寻找左侧边界的二分搜索三、寻找右侧边界的二分查找总结 一、寻找一个数(基本的二分搜索) 这个场景是最简单的,可能也是大家最熟悉的,即搜索一个数&#xf…

2023年天津财经大学珠江学院专升本经济学专业课考试大纲

天津财经大学珠江学院2023年高职升本科专业课考试《经济学》考试大纲一、本大纲系天津财经大学珠江学院2023年高职升本科《经济学》课程考试大纲。所列考试范围出自郑健壮、王培才主编的教材《经济学基础(第二版)》,清华大学出版社&#xff0…

win10通过Docker搭建LNMP环境全流程

win10通过Docker搭建LNMP环境全流程 下载安装docker desktop 由于博主环境已经安装好了,一些异常设置,暂且略过 根据官方教程下载docker desktop执行文件.exe 注意尽量不要把docker安装到C盘,除非你的C盘很大,具体可以参考文章 …

初识 Spring 框架

文章目录一、Spring 介绍二、Spring 下载安装三、编写入门程序1.项目文件构架2.引入相关依赖3.创建实体类4.Spring 配置文件5.编写测试类四、控制反转与依赖注入1.控制反转概念2.依赖注入概念3.依赖注入的类型4.依赖注入的应用一、Spring 介绍 Spring 是由 Rod Johnson 组织和…

计算机毕设题目设计与实现(论文+源码)_kaic

毕业设计(论文)题目 高校图书馆座位预约选座微信小程序设计与实现 基于防火墙的访问控制系统的设计与实现 基于区块链的农产品追溯系统设计与实现 学生公寓楼改造布线系统规划与设计 智能家居网络设计与实现“互联网”农村精准扶贫共享平台的设计与实现“智慧健康少儿成长平台”…

Linux内核——门相关入门知识

为什么20位的寻址可以达到1MB? 🔒 点击查看答案 🔒 拆分如下的段描述符: 0000000000000000 00cf9b000000ffff 00cf93000000ffff 00cffb000000ffff 00cff3000000ffff 80008b04200020ab ffc093dff0000001 0040f30000000fff 0000f20…

TypeScript开启

TypeScript是什么? typescript是以JavaScript为基础构建的语言,是一个Javascript的超集,可以在任何支持JavaScript的平台中执行,typescript扩展了JavaScript,并添加了类型。 注意:ts不能被js直接解析执行&…

MySQL的join你真的了解吗!!!

1.测试用例数据库信息 本文章采用的数据库结构,以及MySQL版本:5.7 t1 表,有一个主键id,字段a,字段b。 (此表建立了一个索引a) 数据大约1000条 t2 表,有一个主键id,字段…

C++标准库分析总结(十一)——<适配器>

目录 1 适配器简介 2 适配器使用分类 2.1 容器适配器 2.2 函数适配器 2.2.1 常见的函数适配器 2.2.2 bind2nd 2.2.3 not1 2.2.4 新型适配器bind 2.3 迭代器适配器 2.3.1 reverse_iterator 2.3.2 insert_iterator 2.4 X适配器 2.4.1 ostream_iterator 2.4.2 istre…

BUUCTF-babyheap_0ctf_2017

checksec 标准堆菜单 IDA Allocate void __fastcall sub_D48(__int64 a1) {int i; // [rsp10h] [rbp-10h]int v2; // [rsp14h] [rbp-Ch]void *v3; // [rsp18h] [rbp-8h]for ( i 0; i < 15; i ){if ( !*(_DWORD *)(24LL * i a1) ){printf("Size: ");v2 sub_1…

【云原生】无VIP稳定性和可扩展性更强的k8s高可用方案讲解与实战操作

文章目录一、概述二、架构三、开始部署1&#xff09;节点信息2&#xff09;前期准备&#xff08;所有节点&#xff09;1、配置hosts2、配置互信3、时间同步4、关闭防火墙5、禁用SELinux6、关闭swap7、设置bridge-nf-call-iptables3&#xff09;安装容器docker&#xff08;所有节…

C++标准库分析总结(十)——<仿函数/函数对象>

目录 1.functor仿函数简介 2 仿函数的分类 3 仿函数使用 4 仿函数可适配的条件 1.functor仿函数简介 仿函数是STL中最简单的部分&#xff0c;存在的本质就是为STL算法部分服务的&#xff0c;一般不单独使用。仿函数&#xff08;functors&#xff09;又称为函数对象&…

Windows 命令行cmd.exe简单介绍

介绍&#xff1a; 在windows系统中&#xff0c;Windows命令shell&#xff08;cmd.exe&#xff09;,在 SystemRoot/System32目录下。 启动命令行&#xff0c;在"开始"——>"搜索"中输入cmd&#xff0c;此时命令行展示当前工作目录&#xff0c;默认为/u…