iOS过场动画调研笔记

前言

因项目需要,最近一段时间都在调研iOS的过场动画。对于我来说这是一个之前没有太涉及的领域,所以有必要把调研的过程和自己的一些理解纪录下来

为什么要自定义过场动画?

如果大家有关注Material Design和最近一些知名App(如快的、一号专车等)的界面设计和交互的变化,就会发现一种新的趋势:平滑的页面过渡。目的旨在于让用户尽量少地感觉到页面之间生硬的切换,从而使App的体验更加流畅。而iOS原生的两种常用过场:Push/Pop和Present,和目前流行的趋势显然是不太符合的,所以自定义过场动画的意义就体现出来了。

Transition-iOS的自定义过场

简介

因为之前有博客对自定义过场做了非常详细的介绍,我就不赘述了,具体参照这里 iOS7之定制View Controller切换效果 (PS:感谢作者)。作者的demo我也有下载看过,他是为每个过场动画封装了单独的类,然后在UIViewController中实现过场切换的代理,在代理中返回相应的动画效果。对于为过场动画封装单独的类这点我是非常赞同的,但是在UIViewController中实现过场切换的代理这一点我觉得不是特别理想,所以后来我的实现做了修改,最终的效果是在UIViewController中只需要调用一个接口,就可以实现自定义过场的效果。

我的设计

分析

首先,我封装了一个单例模式MBTransition基类,使用单例模式的原因有两个:

  1. 在一个App中,同时存在的过场只会有一个。
  2. 实现成单例之后过场对象就不需要依赖于某个UIViewController。

然后.m文件中为这个类实现过场动画的几个代理

#pragma mark UINavigationControllerDelegate methods

// Push/Pop时自定义过场的代理
// 参数:
//      navigationController:导航
//      operation:导航的操作:Push/Pop/None,可以用来控制在哪种导航的操作下使用自定义过场
//      fromVC:执行Push操作的UIViewController
//      toVC:被Push的UIViewController
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                  animationControllerForOperation:(UINavigationControllerOperation)operation
                                               fromViewController:(UIViewController *)fromVC
                                                 toViewController:(UIViewController *)toVC {
    return self;
}

// Present时自定义过场的代理
// 参数:
//      presented:被Present的UIViewController
//      presenting:正在执行Present的UIViewController
//      source:发起Present的UIViewController(PS:正在执行Present和发起Present的UIViewController是有区别的,如果source是某个UINavigationController下的一个UIViewController,那么presenting就是这个UINavigationController,如果source不是在类似UINavigationController或者UITabbarController这样的控件内,那么presenting就是source本身)
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
    return self;
}

// Dismiss时自定义过场的代理
// 参数:
//      dismissed:被Dismiss掉的UIViewController
-(id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    return self;
}

#pragma mark - UIViewControllerContextTransitioning

// 实现具体自定义过场动画效果的代理,这个代理也是实现动画效果的核心
// 参数:
//      transitionContext:过场时的上下文信息
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
}

// 过场动画时间的代理
// 参数:
//      transitionContext:过场时的上下文信息
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return self.duration;
}

通过上面几个代理我们可以知道:Push/Pop和Present时过场动画的代理是不一样的,所以我建立了一个过场类型的枚举,用来控制自定义过场在哪种交互下可用:

typedef enum TransitionType{
    TransitionTypePush, // Push/Pop过场
    TransitionTypePresent // Present过场
}TransitionType;

然后在- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext代理中我们返回了self.duration,所以我们需要在.h文件中添加一个变量来保存过场动画持续的时间:

@property (nonatomic, assign) NSTimeInterval duration;

接下来我们分析一下 (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext 代理中我们做的一些事情:

  1. 通过 transitionContext 获取到过场时切换的两个UIViewController

    // 这里的 fromVC 和 toVC 代表的是过场是由 fromVC 切换到 toVC 的。
    // 比如从A界面Push到B界面时,这里的fromVC是A界面,toVC是B界面,而当B界面被Pop到A界面时,这里的fromVC就是B界面,toVC就是A界面
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
  2. 通过 transitionContext 获取到执行切换的UIView
    // 所有的切换动画都是基于container来实现的
    UIView *container = [transitionContext containerView];
  3. 通过 transitionContext 获取到过场的持续时间
    NSTimeInterval duration = [self transitionDuration:transitionContext];
  4. 最后通过 transitionContext 获取到过场时切换的 fromVC 和 toVC 和Push/Present时保存的 fromVC 和 toVC 进行比较就可以知道目前执行的是Push/Present还是Pop/Dismiss,从而可以为Push/Present和Pop/Dismiss定制不同的动画效果。
    - (BOOL)isReversed:(UIViewController *)fromVC ToVC:(UIViewController *)toVC
    {
        return !([self.fromVC class] == [fromVC class] && [self.toVC class] == [toVC class]);
    }

    所以我们需要在.h文件中添加两个成员变量来保存Push/Present时的 fromVC 和 toVC :

    @property (nonatomic, weak) UIViewController *fromVC;
    @property (nonatomic, weak) UIViewController *toVC;
  5. 接下来就是具体的过场动画部分了,其实就是结合fromVC的view、toVC的view和container做一些动画效果,因为跟做普通的动画没有什么区别,所以这个部分我就不具体描述了。

最后是提供给外部调用的接口,内容如下:

- (void)setTransitionWithFromViewController:(UIViewController *)fromVC ToViewController:(UIViewController *)toVC TransitionType:(TransitionType)type Duration:(NSTimeInterval)duration{

    self.fromVC = fromVC;
    self.toVC = toVC;
    self.duration = duration;
    if (type == TransitionTypePush) {
        self.fromVC.navigationController.delegate = self;
    }else if (type == TransitionTypePresent){
        self.fromVC.transitioningDelegate = self;
        self.toVC.transitioningDelegate = self;
    }
}

上面代码片段所做的事情就是对一些参数进行保存,然后根据 TransitionType 来设置相应的代理。

特点

  1. 如果要实现其他自定义过场,只需要继承MBTransition,然后重写 (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext 代理即可。
  2. 使用者只需调用一个接口即可实现自定义过场,降低了代码耦合。

交互式的切换动画

交互式动画主要是指用户可以通过手势控制整个动画的过程,这个部分我目前还没有研究…

碰到的一些坑

  1. 当UIViewController是UITabbarController或者UINavigationController的一个childViewController时,通过 [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey][transitionContext viewControllerForKey:UITransitionContextToViewControllerKey] 拿到的UIViewController其实是UITabbarController或者UINavigationController,所以在调用接口时,要注意传入的 fromVC 和 toVC 其实是这个UIViewController的UITabbarController或者UINavigationController。
  2. 如果采用Push/Pop的方式,当fromVC属于UITabbarController的一个childViewController,且在 toVC 上不能显示UITabbarController的UITabBar时,UITabbarController的UITabBar会造成很大的麻烦:
    • 如果使用设置hidesBottomBarWhenPushed为true的方式,那么UITabBar的动画不能定制,只能是默认的从右到左和从左到右。
    • 如果使用自定义的方式显示和隐藏UITabBar,因为AutoLayout的原因,后期问题会更多…

    所以在这种情况下建议使用Present的方式切换到新的界面,当然如果大家有好的解决方法也希望能分享给我,谢谢!

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-09 12:53:25

iOS过场动画调研笔记的相关文章

iOS核心动画工作笔记

1.图层和UIVIew的区别:图层不能和用户进行交互,图层性能高 2.imageVIew的图片显示是在图层上面的子层.用maskBounds剪切时剪的是图层,用户看不到是因为子层挡住了 3.CAlayer的代理方法没有协议,任何对象都能成为他的代理,即NSObject的方法 4.UIVIew内部的根图层的代理就是View本身,所以在UIVIew中的drawRect方法绘图.一个view不能设置代理.因为已经是它图层的代理 5.Core Animation直接作用于CALayer,缺点是动画后图片

iOS之动画学习笔记二

今天,我趁着项目空暇之余,把有关CAAnimation以及它的子类的相关属性和方法都罗列一遍.以便将来在忘记的时候能够快速拾起. 一.CAAnimation(The base animation class) 它有两个私有属性: void *_attr; uint32_t _flags; // 暂时不知道它的用途 -.- 以后补上. + (instancetype)animation; // 创建动画实例对象的工厂方法 + (nullable id)defaultValueForKey:(NSS

iOS之动画学习笔记一

iOS复杂动画都是和贝塞尔曲线结合在一起的.因此要学会iOS动画,必须先理解贝塞尔曲线.贝塞尔曲线的教程网上很多,这里就不过多的阐述.主要还是来讲讲有关动画方面的东西. 一.画一条简单的曲线 我们先准备一条波浪形的贝塞尔曲线: CGPoint startPoint = CGPointMake(50, 300); CGPoint endPoint = CGPointMake(300, 300); CGPoint onePoint = CGPointMake(150, 200); CGPoint t

【iOS开发每日小笔记(十二)】仿Facebook登录界面 错误提示抖动 利用CAAnimation设置动画效果

这篇文章是我的[iOS开发每日小笔记]系列中的一片,记录的是今天在开发工作中遇到的,可以用很短的文章或很小的demo演示解释出来的小心得小技巧.它们可能会给用户体验.代码效率得到一些提升,或是之前自己没有接触过的技术,很开心的学到了,放在这里得瑟一下.90%的作用是帮助自己回顾.记忆.复习. 原本以为国庆假期可以有时间看看书,写写博客.实际上大部分时间都被赶场参加婚礼和到处去亲戚家串门吃饭所占用.眼看明天还剩最后一天时间,今天赶紧来更新一篇,也算是没有完全荒废这7天长假吧! Facebook的客

绘图与动画学习笔记(一)

1. 处理图形与动画的框架有 UIKit 高层次的框架,允许开发人员创建视图.窗口.按钮和其他UI相关的组件.它还将一些低级别的API引入到易于使用的高级别API中 Quartz 2D iOS上绘图的主要引擎:UIKit就使用Quartz. Core Graphics 它支持图形上下文.加载图像.绘制图像,等等. Core Animation 顾名思义,是一个帮助开发者在IOS上实现动画的框架 2. UIColor的set方法可设置Graphical context的颜色 - (void)dra

IOS图层Layer学习笔记(二)—— CALayer(上)

IOS图层Layer学习笔记(二)-- CALayer(上) 简介 CALayer是所有图层的基类.主要是一些基本显示属性(位置.锚点.颜色.透明度等).层次关系(子图层和父图层).基本动画等. 接下来分别从常用属性.类方法和实例方法来介绍CALayer的使用.顺序是按头文件的排序来. 常用属性 bounds CGRect,Animatable.控制layer的大小,其中x和y无效果,默认是(0,0). position CGPoint,Animatable.控制layer锚点在父图层的位置.

【iOS开发每日小笔记(三)】利用iOS7 UIKit Dynamics 仿Zaker客户端首页动态效果

这篇文章是我的[iOS开发每日小笔记]系列中的一片,记录的是今天在开发工作中遇到的,可以用很短的文章或很小的demo演示解释出来的小心得小技巧.该分类的文章,内容涉及的知识点可能是很简单的.或是用很短代码片段就能实现的,但在我看来它们可能会给用户体验.代码效率得到一些提升,或是之前自己没有接触过的技术,很开心的学到了,放在这里得瑟一下(^_^).其实,90%的作用是帮助自己回顾.记忆.复习.如果看官觉得太easy,太碎片,则可以有两个选择:1,移步[iOS探究]分类,对那里的文章进行斧正:2,在

IOS气泡动画教程

IOS气泡动画教程 个人翻译:By Tang,原文地址:iOS Bubble Animation Tutorial,可能要翻墙才能访问,版权属于原作者,只做翻译的搬运工. 前面两段文字,基本上就是说明这个动画产生的原因,和本文实现基本没什么关系,就不翻译了,直接贴上效果图如下: 这个获得了一个巨大的成功,收到的第一个评论是: Jackrabbit团队或许十分后悔让我负责这个项目,过去3个月我下了要实现海底中动员中气泡效果的决定. 实际上,我之所以决心实现气泡动画,是因为我之前创建了一个名叫JRM

[iOS]过渡动画之高级模仿 airbnb

注意:我为过渡动画写了两篇文章:第一篇:[iOS]过渡动画之简单模仿系统,主要分析系统简单的动画实现原理,以及讲解坐标系.绝对坐标系.相对坐标系,坐标系转换等知识,为第二篇储备理论基础.最后实现 Mac 上的文件预览动画.第二篇:[iOS]过渡动画之高级模仿 airbnb,主要基于第一篇的理论来实现复杂的界面过渡,包括进入和退出动画的串联.最后将这个动画的实现部分与当前界面解耦,并封装为一个普适(其他类似界面也适用)的工具类. 这两篇文章将会带你学到如何实现下图 airbnb 首页类似的过渡动画