自定义过渡

前言

对于Apple默认的转场动画(push、pop、modal)我们再熟悉不过了,但是这些显得单调的风格早已无法满足日益挑剔的用户了,对于极其重视用户体验的Apple肯定是不会让这个问题遗留下去的。早在iOS7,Apple就开放了视图控制器转场的API,到如今又迭代了三代iOS,相应的API已经很稳定了。如果是注意App体验的朋友一定发现了目前市面上的App导航控制器的转场绝大多数都是系统的默认实现,即经典的push、pop。它们最大的好处就是简单方便,对于功能性的App其实就已经够了,但是对于娱乐性的了?

娱乐性的App通常都具有良好的UI设计以及酷炫的视觉效果,这两者的合理融合是它们相互竞争的利器,同时也决定了用户的取向。像风靡全球的Angry Birds,笔者第一次接触这个游戏是那真是眼睛发亮,根本停不下来啊!所以为了今后的App能脱颖而出,就先定一个小目标——征服自定义过渡!

体会Apple的架构

UIPresentationController

UIPresentationController是iOS8新增加的类,以下是Apple官方文档对它的简介:

A UIPresentationController object provides advanced view and transition management for presented view controllers. From the time a view controller is presented until the time it is dismissed,UIKit uses a presentation controller to manage various aspects of the presentation process for that view controller. The presentation controller can add its own animations on top of those provided by animator objects,it can respond to size changes,and it can manage other aspects of how the view controller is presented onscreen.

大意为:

UIPresentationController对象对于呈现的视图控制器提供了高级的视图和过渡管理。从一个视图控制器呈现时到退场时,UIKit对于这个控制器使用了一个展示控制器来管理它各个方面的呈现过程。展示控制器可以添加那些由动画对象提供的自己的动画,它可以响应尺寸大小变化,并且它还可以管理视图控制器是如何呈现在屏幕上的其他方面的事情。

毫无疑问,从官方文档里面我们可以得到如何使用它的一切信息,但是谁又能说我们学习编程不是天生的“弱势群体”了。(ps:多希望我是混血儿啊!)笔者接下来的内容就不再引用官方文档,有需要的朋友请点Apple后面的官方文档。

对于系统默认的pop、push、modal相应的也有默认UIPresentationController对象行为默认的实现,当一个视图控制器被呈现时,UIKit就会调用视图控制器关联的UIPresentationController对象的presentationTransitionWillBegin()方法,当呈现阶段结束后就会调用其presentationTransitionDidEnd(_:)来告诉你过渡结束。对于文章将要给出的Demo在这里面的代码将是这样:

//When a view controller is about to be presented,UIKit calls the presentation controller’s presentationTransitionWillBegin() method.
    override func presentationTransitionWillBegin() {
        dismissView.frame = self.containerView!.bounds
        dismissView.alpha = 0.0

        self.containerView!.addSubview(dismissView)
        self.containerView!.addSubview(self.presentedView()!)

        let transitionCoordinator = self.presentedViewController.transitionCoordinator()!
        transitionCoordinator.animateAlongsideTransition({ (context: UIViewControllerTransitionCoordinatorContext) in
            self.dismissView.alpha = 1.0
            },completion: nil)
    }

//At the end of the presentation phase,UIKit calls the presentationTransitionDidEnd(_:) method to let you know that the transition finished.
    override func presentationTransitionDidEnd(completed: Bool) {
        if !completed {
            dismissView.removeFromSuperview()
        }
    }

当视图控制器退场时,也会调用UIPresentation对象的dismissalTransitionWillBegin()dismissalTransitionDidEnd(_:)方法,就像这样:

override func dismissalTransitionWillBegin() {
        let transitionCoordinator = self.presentedViewController.transitionCoordinator()!
        transitionCoordinator.animateAlongsideTransition({ (context: UIViewControllerTransitionCoordinatorContext) in
            self.dismissView.alpha = 0.0
            },completion: nil)
    }

    override func dismissalTransitionDidEnd(completed: Bool) {
        if completed {
            dismissView.removeFromSuperview()
        }
    }

同时视图控制器被呈现的 view 在过渡动画结束后的最终位置也是由 UIPresentationViewController 来负责给定的,我们只需要重载frameOfPresentedViewInContainerView()就可以了:

override func frameOfPresentedViewInContainerView() -> CGRect {
        var frame = self.containerView!.bounds
        frame = CGRectInset(frame,50.0,200.0)

        return frame
    }

如果你还想让界面的尺寸能适应屏幕的旋转,还需要在viewWillTransitionToSize(size:coordinator:)方法里做出一些改变:

override func viewWillTransitionToSize(size: CGSize,withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransitionToSize(size,withTransitionCoordinator: coordinator)

        coordinator.animateAlongsideTransition({ (context: UIViewControllerTransitionCoordinatorContext) in
            self.dismissView.frame = self.containerView!.bounds
            },completion: nil)
    }

(ps:笔者忽略一个细节——整个界面是在storyboard里搭建的,加上用了所有竖屏的sizeClass限定,导致旋转之后就只有transition后的半透明的黑色遮罩能适应屏幕的旋转,其他的都“不翼而飞”,真是抱歉!)

UIViewControllerAnimatedTransitioning

就像我们看到的这样,它不是以明显的OC类名或者部分类名结尾的API。正如各位想得这样,它只是一个协议,一个负责动画过渡的协议。而且它的协议方法也只有两个:

  • transitionDuration(_:):负责返回动画的过渡时间
  • animateTransition(_:):负责动画过渡的具体行为

前者相当直观,笔者不再赘述。后者会传回一个参数——过渡上下文(transitionContext),通过对应的键我们可以访问实现过渡所必须的一些对象:

  • viewControllerForKey:访问过渡前后的视图控制器
  • containerView:访问过渡当前控制器的视图(在哪个控制器里访问就获得哪个控制器的视图)
  • viewForKey:访问过渡前后控制器的视图(与前者的不同在于可以在一个控制器中访问其过渡前后的视图)
  • finalFrameForViewController、initialFrameForViewController:获取过渡前后控制器过渡开始和结束的frame

UIViewControllerTransitioningDelegate

如果你想掌控自己的视图控制器的过渡方式,那么就应该让控制器遵守标题所指的协议,并重新设置它的过渡代理。文章的Demo中将控制器的模态视图的风格设置为自定义,因为Demo展示的正是模态视图的自定义过渡,否则将会和系统的风格冲突,而这将为你展现iOS的“凌乱美”。Demo中使用了如下方法:

  • presentationControllerForPresentedViewController(_:presentingViewController:sourceViewController:):iOS8新增方法,给即将要被呈现的视图控制器自身指定UIPresentationController对象,来管理过渡前后的视图切换
  • animationControllerForPresentedController(_:presentingViewController:sourceViewController:):给即将要被呈现的视图控制器自身指定过渡的动画
  • animationControllerForDismissedController(_:):给即将退场的视图控制器自身指定过渡动画

下面通过几张图片(来源)来说明presentedViewController、presentingViewController:

可以和用户交互的ViewController叫做presentedViewController,也就是过渡的目的视图控制器,在Demo中也就是MessageViewController。presentingViewController就是后面被部分遮盖的UIViewController,即Demo中的ViewController。presentedViewController就是要呈现(presentation)的content,所有的UIViewController的 呈 现 从iOS8开始由UIPresentationController来管理,它定义了content和chrome的动画,chrome可以理解为presentedViewController与presentingViewController的隔离层,即Demo中的半透明的黑色遮罩:

你可能还注意到了sourceController,它其实就是presentingController,可以将它理解为触发过渡的源控制器,Demo中得到了验证:

func presentationControllerForPresentedViewController(presented: UIViewController,presentingViewController presenting: UIViewController,sourceViewController source: UIViewController) -> UIPresentationController? {
        if presented == self {
            //presented: MessageViewController source: ViewController
            print("presented: \(presented),source:\(source)")

            return CustomPresentationController(presentedViewController: presented,presentingViewController: presenting)
        } else {
            return nil
        }
    }

    func animationControllerForPresentedController(presented: UIViewController,presentingController presenting: UIViewController,sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        if source == presenting {
            print("equal")
        }
        if presented == self {
            print("presented: \(presented)),presenting: \(presenting) source:\(source)")

            return CustomAnimationController(isPresenting: true)
        } else {
            return nil
        }

    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        if dismissed == self {
            //dismissed: MessageViewController
            print("dismissed: \(dismissed)")

            return CustomAnimationController(isPresenting: false)
        } else {
            return nil
        }
    }

最终效果图:

项目源代码在这里,由于SwiftAPI的不稳定,原作者的Demo已经不能正常运行,为此笔者做了新的调整以便正常运行。同时这里有篇扩展资料,是关于自定义导航过渡的文章(OC实现),并讲解了如何构建可交互性的过渡,文章的Demo也很有使用价值,值得花时间去品读!

总结

“告诉我过渡前后的动画方式和所要展示的内容,我就按照你的要求完成过渡”,感谢Apple的UIKit团队所作出的努力,让我们能以清晰的思路和优雅的代码架构完成我们所想要的过渡效果。细想一下,凡此种种,不就是良性App架构的体现吗?

参考:
http://nonomori.farbox.com/post/ios-8-presentation-controller

写在后面的扩展资料:

唐巧的技术博客:iOS视图控制器转场详解

引用其中的一句话:

转场动画的本质是对即将消失的当前视图和即将出现的下一视图进行动画。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘贴.待开发的功能:1.支持自动生成约束2.开发设置页面3.做一个浏览器插件,支持不需要下载整个工程,可即时操作当前蓝湖浏览页面4.支持Flutter语言模板生成5.支持更多平台,如Sketch等6.支持用户自定义语言模板
现实生活中,我们听到的声音都是时间连续的,我们称为这种信号叫模拟信号。模拟信号需要进行数字化以后才能在计算机中使用。目前我们在计算机上进行音频播放都需要依赖于音频文件。那么音频文件如何生成的呢?音频文件的生成过程是将声音信息采样、量化和编码产生的数字信号的过程,我们人耳所能听到的声音频率范围为(20Hz~20KHz),因此音频文件格式的最大带宽是20KHZ。根据奈奎斯特的理论,音频文件的采样率一般在40~50KHZ之间。奈奎斯特采样定律,又称香农采样定律。...............
前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿遍又亿遍,久久不能离开!看着小仙紫姐姐的蹦迪视频,除了一键三连还能做什么?突发奇想,能不能把舞蹈视频转成代码舞呢?说干就干,今天就手把手教大家如何把跳舞视频转成代码舞,跟着仙女姐姐一起蹦起来~视频来源:【紫颜】见过仙女蹦迪吗 【千盏】一、核心功能设计总体来说,我们需要分为以下几步完成:从B站上把小姐姐的视频下载下来对视频进行截取GIF,把截取的GIF通过ASCII Animator进行ASCII字符转换把转换的字符gif根据每
【Android App】实战项目之仿抖音的短视频分享App(附源码和演示视频 超详细必看)
前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至2022年4月底。我已经将这篇博客的内容写为论文,上传至arxiv:https://arxiv.org/pdf/2204.10160.pdf欢迎大家指出我论文中的问题,特别是语法与用词问题在github上,我也上传了完整的项目:https://github.com/Whiffe/Custom-ava-dataset_Custom-Spatio-Temporally-Action-Video-Dataset关于自定义ava数据集,也是后台
因为我既对接过session、cookie,也对接过JWT,今年因为工作需要也对接了gtoken的2个版本,对这方面的理解还算深入。尤其是看到官方文档评论区又小伙伴表示看不懂,所以做了这期视频内容出来:视频在这里:本期内容对应B站的开源视频因为涉及的知识点比较多,视频内容比较长。如果你觉得看视频浪费时间,可以直接阅读源码:goframe v2版本集成gtokengoframe v1版本集成gtokengoframe v2版本集成jwtgoframe v2版本session登录官方调用示例文档jwt和sess
【Android App】实战项目之仿微信的私信和群聊App(附源码和演示视频 超详细必看)
用Android Studio的VideoView组件实现简单的本地视频播放器。本文将讲解如何使用Android视频播放器VideoView组件来播放本地视频和网络视频,实现起来还是比较简单的。VideoView组件的作用与ImageView类似,只是ImageView用于显示图片,VideoView用于播放视频。...
采用MATLAB对正弦信号,语音信号进行生成、采样和内插恢复,利用MATLAB工具箱对混杂噪声的音频信号进行滤波
随着移动互联网、云端存储等技术的快速发展,包含丰富信息的音频数据呈现几何级速率增长。这些海量数据在为人工分析带来困难的同时,也为音频认知、创新学习研究提供了数据基础。在本节中,我们通过构建生成模型来生成音频序列文件,从而进一步加深对序列数据处理问题的了解。
基于yolov5+deepsort+slowfast算法的视频实时行为检测。1. yolov5实现目标检测,确定目标坐标 2. deepsort实现目标跟踪,持续标注目标坐标 3. slowfast实现动作识别,并给出置信率 4. 用框持续框住目标,并将动作类别以及置信度显示在框上
数字电子钟设计本文主要完成数字电子钟的以下功能1、计时功能(24小时)2、秒表功能(一个按键实现开始暂停,另一个按键实现清零功能)3、闹钟功能(设置闹钟以及到时响10秒)4、校时功能5、其他功能(清零、加速、星期、八位数码管显示等)前排提示:前面几篇文章介绍过的内容就不详细介绍了,可以看我专栏的前几篇文章。PS.工程文件放在最后面总体设计本次设计主要是在前一篇文章 数字电子钟基本功能的实现 的基础上改编而成的,主要结构不变,分频器将50MHz分为较低的频率备用;dig_select
1.进入官网下载OBS stdioOpen Broadcaster Software | OBS (obsproject.com)2.下载一个插件,拓展OBS的虚拟摄像头功能链接:OBS 虚拟摄像头插件.zip_免费高速下载|百度网盘-分享无限制 (baidu.com)提取码:6656--来自百度网盘超级会员V1的分享**注意**该插件必须下载但OBS的根目录(应该是自动匹配了的)3.打开OBS,选中虚拟摄像头选择启用在底部添加一段视频录制选择下面,进行录制.
Meta公司在9月29日首次推出一款人工智能系统模型:Make-A-Video,可以从给定的文字提示生成短视频。基于**文本到图像生成技术的最新进展**,该技术旨在实现文本到视频的生成,可以仅用几个单词或几行文本生成异想天开、独一无二的视频,将无限的想象力带入生活
音频信号叠加噪声及滤波一、前言二、信号分析及加噪三、滤波去噪四、总结一、前言之前一直对硬件上的内容比较关注,但是可能是因为硬件方面的东西可能真的是比较杂,而且需要渗透的东西太多了,所以学习进展比较缓慢。因为也很少有单纯的硬件学习研究,总是会伴随着各种理论需要硬件做支撑,所以还是想要慢慢接触理论学习。但是之前总找不到切入点,不知道从哪里开始,就一直拖着。最近稍微接触了一点信号处理,就用这个当作切入点,开始接触理论学习。二、信号分析及加噪信号处理选用了matlab做工具,选了一个最简单的语音信号处理方
腾讯云 TRTC 实时音视频服务体验,从认识 TRTC 到 TRTC 的开发实践,Demo 演示& IM 服务搭建。
音乐音频分类技术能够基于音乐内容为音乐添加类别标签,在音乐资源的高效组织、检索和推荐等相关方面的研究和应用具有重要意义。传统的音乐分类方法大量使用了人工设计的声学特征,特征的设计需要音乐领域的知识,不同分类任务的特征往往并不通用。深度学习的出现给更好地解决音乐分类问题提供了新的思路,本文对基于深度学习的音乐音频分类方法进行了研究。首先将音乐的音频信号转换成声谱作为统一表示,避免了手工选取特征存在的问题,然后基于一维卷积构建了一种音乐分类模型。
C++知识精讲16 | 井字棋游戏(配资源+视频)【赋源码,双人对战】
本文主要讲解如何在Java中,使用FFmpeg进行视频的帧读取,并最终合并成Gif动态图。
在本篇博文中,我们谈及了 Swift 中 some、any 关键字以及主关联类型(primary associated types)的前世今生,并由浅及深用简明的示例向大家讲解了它们之间的奥秘玄机。