iOS UIKit:viewController之动画(5)

当弹出一个view controller时,UIKit提供了一些标准转换动画,并且也支持用户自定义的动画效果。

1 UIView动画

UIView是自带动画实现功能,其中有两种方式实现:

       1) animateWithDuration系列方法

       2) transitionFromViewController方法

1.1 animateWithDuration

只要在该方法的animations block中修改UIView的动画属性,那么即可实现动画变换,所以为container viewController中实现不同view controller之间的动画切换,只要改变了UIView的动画属性即可以实现变换。

如在调用viewMove方法之前,fromVC为显示状态,而toVC为未显示状态。所以调用时即可实现动画切换:

1 -(void) viewMove:(UIViewController*) fromVC:(UIViewController*)toVC
 2 {
 3     [UIView animateWithDuration:1 animations:^{
 4         fromVC.view.alpha = 0;
 5         toVC.view.alpha = 1;
 6     } completion:^(BOOL finished) {
 7         fromVC.view.alpha = 0;
 8         toVC.view.alpha = 1;
 9     }];
10 }

1.2 transitionFromViewController

UIView同时提供了视图切换方法,同时可支持动画变换,即transitionFromViewController方法。从而可以在container viewController中调用该方法来实现视图的变换。

如在当前viewController有两个子视图控制器:fromVC和toVC,并且fromVC为显示状态。所以可以调用transitionFromViewController方法来实现动画切换。

1 -(void) viewMove:(UIViewController*) fromVC:(UIViewController*)toVC
2 {
3 [self transitionFromViewController:fromVC toViewController:toVC duration:1
4     options:UIViewAnimationOptionTransitionFlipFromLeft |UIViewAnimationOptionShowHideTransitionViews 
5     animations:nil completion:nil
6 ];
7 }

2 转换动画次序

转换动画是交互两个view controller的内容,其中存在两种转换类型:弹出(presentation)和撤回(dismissal)。

1) presentation:这种动画是指在app的view controller层次结构中添加新的view controller;

2) dismissal:这种动画是指从app的view controller层次结构中移除view controller。

2.1 转换delegate

转换delegate是一个实现了UIViewControllerTransitioningDelegate协议的对象,其工作是提供如下之一的对象:

       1) Animator对象:

该对象是实现了UIViewControllerAnimatedTransitioning协议的实体。其功能是负责以动画的形式显示或隐藏view controller的视图。转换delegate向那些进行presenting 和dismissing操作的实体提供Animator对象。

      2) Interactive animator对象:

该对象是实现了UIViewControllerInteractiveTransitioning协议的实体。其也是实现动画操作,但可在动画过程中与用户进行事件交互。

      3) Presentation controller:

当view controller在屏幕时,presentation controller负责管理弹出(presentation)的样式。系统已提供了一些内置样式,同时也支持用户自定义更多的presentation样式。

若要实现自定义动画,需要设置新弹出view controller的transitioningDelegate属性为遵守UIViewControllerTransitioningDelegate协议的对象,同时要修改新弹出view controller的modalPresentationStyle属性常量UIModalPresentationCustom

图 312 The custom presentation and animator objects

如图 312所示,绿色表示 Presenting view controller(主动弹出),蓝色为Presented view controller(被动弹出),被弹出的VC有个Transitioning Delegate属性,通过这个属性可获得Animator、Interactive Animator和Presentation Controller对象。

2.2 执行时序

1) Present view controller

当被弹出view controller的transitioningDelegate属性是一个有效对象时,UIKit将按指定的动画弹出这个view controller。在准备阶段UIKit会从UIViewControllerTransitioningDelegate协议(称为转换delegate)调用错误! 超链接引用无效。:方法,从而来查询指定的动画对象,若该对象有效,则UIKit会按如下步骤执行:

a) UIKit会调用转换delegate的interactionControllerForPresentation:方法,若该方法返回nil,那么UIKit将不与用户进行交互的执行动画。

b) UIKit调用animator对象的transitionDuration:方法来查询动画的执行时间。

c) UIKit调用相应方法开始执行动画:

*若为非交互动画,则调用animator对象的animateTransition:方法

*若为交互动画,则调用interactive animator的startInteractiveTransition:方法

d) UIKit等待动画对象调用context transitioning的completeTransition:方法来等完成动画。

在自定义的动画中,需要手动调用transitionContext对象的completeTransition方法来完成动画操作。

2) Dismiss view controller

当消除一个view controller时,UIKit会调用UIViewControllerTransitioningDelegate协议(称为转换delegate)中的animationControllerForDismissedController:方法,并按如下步骤执行程序:

a) UIKit会调用转换delegate的interactionControllerForDismissal:方法,若该方法返回nil,那么UIKit将不与用户进行交互的执行动画。

b) UIKit调用animator对象的transitionDuration:方法来查询动画的执行时间。

c) UIKit调用相应方法开始执行动画:

*若为非交互动画,则调用animator对象的animateTransition:方法

*若为交互动画,则调用interactive animator的startInteractiveTransition:方法

d) UIKit等待动画对象调用context transitioning的completeTransition:方法来等完成动画。

在自定义的动画中,需要手动调用transitionContext对象的completeTransition方法来完成动画操作。

注意:

当在实现动画体结束时必须手动调用completeTransition:方法来结束操作。

2.3 转换Context对象

在转换动画开始之前,UIKit会创建一个transitioning context对象,并将一些如何执行动画的信息填充到该对象中,这些信息包括Presenting view controller、Presented view controller、container view controller,以及是否与用户进行交互的信息。其中context对象是实现了UIViewControllerContextTransitioning协议

如图 313所示,白色方块表示container view(不是controller)、绿色表示Presenting view controller、蓝色表示Presented view controller,虚线表示引用,实线表示方法。Animator对象通过animateTransition:方法获得context对象,而context通过自身的containerView:方法获得container VC。

图 313 The transitioning context object

2.4 转换Coordinator对象

不管是内置的转换还是自定义的转换,UIKit都会创建一个Coordinator对象帮助动画的执行。除了Present和dismissal view controller外,转换还可能发生在view cotroller的frame发生变化时。在动画过程中,可以通过Coordinator对象获得一些信息。如图 314所示,图形语义与图 313一样。

图 314 The transition coordinator objects

3 使用自定义动画

使用自定义的动画实现Present(弹出)view controller,需要进行如下操作:

1)创建一个Presented view controller;

2)创建一个已经实现了UIViewControllerTransitioningDelegate协议的对象,并将其赋值给Presented view controller对象的transitioningDelegate属性

3)调用presentViewController:animated:completion:方法弹出新的view controller,并且需要将YES传递给该方法的animated参数。

4 实现转换delegate

如图 312所示,转换delegate的功能是为Present和dismissal操作,提供Animator、Interactive animator

和Presentation controller三种对象。所以实现UIViewControllerTransitioningDelegate协议也就是实现那些能够获取这三个对象的方法。

如下是实现一个能获取Animator对象的方法:

1 - (id<UIViewControllerAnimatedTransitioning>)
2             animationControllerForPresentedController:(UIViewController *)presented
3                                 presentingController:(UIViewController *)presenting
4                                   sourceController:(UIViewController *)source 
5     {
6             MyAnimator* animator = [[MyAnimator alloc] init];
7             return animator;
8 }

5 实现动画delegate

为了实现UIViewControllerAnimatedTransitioning协议需要实现两个方法:

      1) transitionDuration: transitionContext:返回动画持续的时间;

      2) animateTransition: transitionContext:执行具体动画的方法;

其中animateTransition方法是重点,主要工作都是在实现该方法中,其中可以将实现该方法分为如下三个步骤:

a) 获取与动画有关的参数;

b) 通过core Animation或UIView animation实现动画;

c) 完成动画操作。

5.1 获取参数

当实现animateTransition方法时,其会传递一个transitionContext参数,可以通过这个参数能够获得与动画相关的信息:

1) 通过viewControllerForKey:方法获得在转换时的"from"和"to" view controller;

2) 通过containerView方法获得动画的超类,从而将所有的子view添加到这个container view中;

3) 通过viewForKey:方法获得被添加或被删除的view,其中在转换过程中要么是被添加,要么是被删除的view。

4) 通过finalFrameForViewController:方法获得最后被添加或删除后view 的frame矩形。

由于动画对象可以向Present和dismissal操作提供动画,所以通过transitionContext获取的"from"和"to" view controller语义有些差异。如图 315所示,当执行Present操作时,是将"to" view controller添加到container层次结构中;而当执行dismissal操作时,是将"from" view controller从container层次结构中移除。

图 315 The from and to objects

5.2 实现动画

为了实现动画效果,在animateTransition:方法中必须完成两部分的内容:

a) 以动画的形式修改presented view controller中的view位置;

b) 将presented view controller中的view添加到container view的层次结构中。

1) Presentation 动画

对于执行弹出动画,可以按如下步骤完成配置:

a) 通过transitionContext对象的viewControllerForKey: viewForKey:方法获得view controllers 和views对象。

b) 设置"to" view的起始位置,同时也可修改其它属性值。

c) 通过transitionContext对象的finalFrameForViewController:方法获取"to" view的最终位置。

d) 将 "to" view添加到container view的层次结构中。

e) 创建动画

  • 在animation block中,修改"to" view为最终的位置。同时也可设置其它属性的最终值;
  • 在completion block中,调用transitionContext对象的completeTransition::方法完成动画。

2) Dismissal动画

消除view controller的动画与弹出的操作相似,同样也可按如下步骤完成配置:

a)通过transitionContext对象的viewControllerForKey: viewForKey:方法获得view controllers 和views对象。

       b) 计算presented view controller的view(即是"from" view)最终位置,该view为被清除的view。

c) 还是将 "to" view添加为container view的子view。

d) 创建动画

  • 在animation block中,修改"from" view为最终的位置。同时也可设置其它属性的最终值;
  • 在completion block中,调用transitionContext对象的completeTransition::方法完成动画。

5.3 测试实例

1) 场景

如要实现图 316所示的两种动画操作,在A视图中弹出B视图,并且可以在B视图将其自身推出返回到A视图。

图 316 A custom presentation and dismissal

2) 实现动画Delegate

用户需要创建已经实现了UIViewControllerAnimatedTransitioning协议的对象,如下是MyAnimator类的具体实现,在该类的animateTransition:方法中实现了两种动画效果(Presentation和Dismissal),其中Presenting为YES时,表示实现Presentation操作;当Presenting为NO时,表示实现Dismissal操作,这个Presenting在构造函数中进行初始化。当然也可以将两种操作分别实现在不同的对象中。

1 @interface MyAnimator : NSObject <UIViewControllerAnimatedTransitioning>
 2 @property bool presenting;
 3 -(id) init:(BOOL)presenting;
 4 @end
 5 @implementation MyAnimator
 6 -(id) init:(BOOL)presenting
 7 {
 8     if(self = [super init])
 9     {
10         self.presenting = presenting;
11     }
12     return (self);
13 }
14 -(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
15 {
16     return 1;
17 }
18 -(void) animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
19 {
20     UIView *containerView = [transitionContext containerView];
21     UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
22     UIViewController *toVC   = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
23     
24     UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
25     UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
26     
27     // Set up some variables for the animation.
28     CGRect containerFrame = containerView.frame;
29     CGRect toViewStartFrame = [transitionContext initialFrameForViewController:toVC];
30     CGRect toViewFinalFrame = [transitionContext finalFrameForViewController:toVC];
31     CGRect fromViewFinalFrame = [transitionContext finalFrameForViewController:fromVC];
32     
33     // Set up the animation parameters.
34     if (self.presenting) {
35         // Modify the frame of the presented view so that it starts
36         // offscreen at the lower-right corner of the container.
37         toViewStartFrame.origin.x = containerFrame.size.width;
38         toViewStartFrame.origin.y = containerFrame.size.height;
39     }
40     else {
41         // Modify the frame of the dismissed view so it ends in
42         // the lower-right corner of the container view.
43         fromViewFinalFrame = CGRectMake(containerFrame.size.width,
44                                         containerFrame.size.height,
45                                         toView.frame.size.width,
46                                         toView.frame.size.height);
47     }
48     
49     // Always add the "to" view to the container.
50     // And it doesn‘t hurt to set its start frame.
51     [containerView addSubview:toView];
52     toView.frame = toViewStartFrame;
53     
54     // Animate using the animator‘s own duration value.
55     [UIView animateWithDuration:[self transitionDuration:transitionContext]
56                      animations:^{
57                          if (self.presenting) {
58                              // Move the presented view into position.
59                              [toView setFrame:toViewFinalFrame];
60                          }
61                          else {
62                              // Move the dismissed view offscreen.
63                              [fromView setFrame:fromViewFinalFrame];
64                          }
65                      }
66                      completion:^(BOOL finished){
67                          BOOL success = ![transitionContext transitionWasCancelled];
68                          
69                          // After a failed presentation or successful dismissal, remove the view.
70                          if ((self.presenting && !success) || (!self.presenting && success)) {
71                              [toView removeFromSuperview];
72                          }
73                          
74                          // Notify UIKit that the transition has finished
75                          [transitionContext completeTransition:success];
76                      }];
77 }

3) 实现转换Delegate

在弹出动画前,需要先给Presented view controller对象设置transitioningDelegate属性,而设置的对象为UIViewControllerTransitioningDelegate协议的实现类。如下是一种实现方式,由于被弹出的view controller有两种动画效果(Presentation和Dismissal),所以需要实现animationControllerForPresentedControlleranimationControllerForDismissedController方法分别弹出两种不同的动画对象。

1 @interface NSObject: UIViewController <UIViewControllerTransitioningDelegate>
 2 @end
 3 @implementation myTransitioningDelegate
 4 - (id<UIViewControllerAnimatedTransitioning>)
 5 animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
 6 {
 7     MyAnimator *animator = [[MyAnimator alloc] init:YES];
 8     
 9     return animator;
10 }
11 -(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
12 {
13     MyAnimator *animator = [[MyAnimator alloc] init:NO];
14     return animator;
15 }
16 @end

4) 实现弹出操作

上述的转换delegate和动画delegate都已经实现完成,接下来就是要实现转换视图的响应方法。即firstViewController为图 316的A视图控制器,而thirdViewController为图 316的B视图控制器。而customAnimation:方法为按钮的响应方法,在该方法中调用presentViewController方法来弹出thirdViewController视图(即B视图)。

其中需要注意的是mtd属性,不能将其声明为customAnimation方法内的局部变量,否则将导致Dismissal操作不能实现动画,因为在该方法推出后,mtd变量的生命周期将结束,从而在Dismissal操作时其已经无效,所以这里将mtd声明为成员属性,当然若firstViewController类自身实现了UIViewControllerAnimatedTransitioning协议,那么可以将transitioningDelegate设置为self。

1 @interface firstViewController : UIViewController <UIViewControllerTransitioningDelegate>
 2 {
 3     myTransitioningDelegate *mtd;
 4 }
 5 @end
 6 - (IBAction)customAnimation:(id)sender {
 7     thirdViewController *thirdVC = [self.storyboard instantiateViewControllerWithIdentifier:@"thirdViewController"];
 8     
 9     mtd = [[myTransitioningDelegate alloc] init];
10     thirdVC.transitioningDelegate = mtd;
11     thirdVC.modalPresentationStyle = UIModalPresentationCustom;
12     
13     [self presentViewController:thirdVC animated:YES completion:nil];
14 }

5) 实现退出操作

在被弹出视图(B视图)中,只需实现退出按钮的响应方法(dismissal),从而在该方法中调用dismissViewControllerAnimated方法推出操作。

1 @implementation thirdViewController
2 - (IBAction)dismissal:(id)sender
3 {
4     [self dismissViewControllerAnimated:YES completion:nil];
5 }
6 @end

6 添加交互对象

UIKit支持添加一个交互对象,通过这个对象用户能在进行弹出等操作时,控制动画的动作。为转换delegate添加一个交互对象也非常简单,只需在其interactionControllerForPresentation方法中返回一个遵守UIViewControllerInteractiveTransitioning协议的对象即可。可以直接实现该协议,也可以继承UIPercentDrivenInteractiveTransition类,但都需重载startInteractiveTransition方法。

最后需要注意的是,由于转换动画还是执行UIViewControllerTransitioningDelegate协议中animationControllerForPresentedController方法返回的对象,但动画开始执行的时间仍是在调用finishInteractiveTransition方法后。

如下所示的实例:

1) 实例场景

用户在点击视图的按钮后,需要再点击视图中的空白区域,才能实现转换及动画。从而本例只是在3.3.5.3小节所示的基础上添加一个交互对象,所以对其它内容都无需修改,只是添加的交互类myInteractiveAnimator,及在myTransitioningDelegate类中添加了一个interactionControllerForPresentation方法,在该方法中返回一个交互对象。

2) 实现交互类

本例采用继承UIPercentDrivenInteractiveTransition类的方式实现交互,从而实现了startInteractiveTransition方法。在该方法中添加了一个点击手势识别器,并在响应方法中启动动画转换。

1 @interface myInteractiveAnimator : UIPercentDrivenInteractiveTransition
 2 @property UITapGestureRecognizer* panGesture;
 3 @end
 4 @implementation myInteractiveAnimator
 5 - (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext
 6  {
 7     // Always call super first.
 8 [super startInteractiveTransition:transitionContext];
 9 // Add the gesture recognizer to the container view.
10     self.panGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeUpdate:)];
11     UIView* container = [transitionContext containerView];
12     [container addGestureRecognizer:self.panGesture];
13 }
14 -(void)handleSwipeUpdate:(UIGestureRecognizer *)gestureRecognizer 
15 {
16     NSLog(@"hello world");
17     [self finishInteractiveTransition];
18 }
19 @end

3) 返回交互对象

如下是在3.3.5.3小节所示的myTransitioningDelegate类基础上添加了interactionControllerForPresentation方法,其它方法都是原来的内容。

1 @implementation myTransitioningDelegate
 2 - (id<UIViewControllerAnimatedTransitioning>)
 3 animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
 4 {
 5     MyAnimator *animator = [[MyAnimator alloc] init:YES];
 6     return animator;
 7 }
 8 -(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
 9 {
10     MyAnimator *animator = [[MyAnimator alloc] init:NO];
11     return animator;
12 }
13 - (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator
14 {
15     myInteractiveAnimator *myAnimator = [[myInteractiveAnimator alloc] init];
16     return myAnimator;
17 }
18 @end

iOS UIKit:viewController文章参考文献

[1] View Controller Programming Guide for IOS

[2] 让iPhone也能popup的效果

[3] 创建Unwind Segue

[4] UIViewControllerTransitioningDelegate Protocol Reference.

[5] UIViewControllerContextTransitioning Protocol Reference.

[6] UIViewControllerInteractiveTransitioning Protocol Referrence

时间: 2024-07-31 14:21:50

iOS UIKit:viewController之动画(5)的相关文章

ios状态栏调整 简单动画的知识点

首先状态栏式view的属性,所以在viewController中直接重写: /** 修改状态栏 */ - (UIStatusBarStyle)preferredStatusBarStyle { // 修改状态栏的颜色(白色) return UIStatusBarStyleLightContent; } // 这种返回值没有*的说明不是对象,那么不是枚举就是结构体,大多数情况是枚举,所以取值也比较简单,一般是返回值后边加上状态: 在UIKit学习中常用的块动画: // 块动画 animateWit

[iOS Animation]-CALayer 定时器动画

定时器的动画 我可以指导你,但是你必须按照我说的做. -- 骇客帝国 在第10章“缓冲”中,我们研究了CAMediaTimingFunction,它是一个通过控制动画缓冲来模拟物理效果例如加速或者减速来增强现实感的东西,那么如果想更加真实地模拟物理交互或者实时根据用户输入修改动画改怎么办呢?在这一章中,我们将继续探索一种能够允许我们精确地控制一帧一帧展示的基于定时器的动画. 定时帧 动画看起来是用来显示一段连续的运动过程,但实际上当在固定位置上展示像素的时候并不能做到这一点.一般来说这种显示都无

[iOS Animation]-CALayer 定时器动画演示二

Run Loop 模式 注意到当创建CADisplayLink的时候,我们需要指定一个run loop和run loop mode,对于run loop来说,我们就使用了主线程的run loop,因为任何用户界面的更新都需要在主线程执行,但是模式的选择就并不那么清楚了,每个添加到run loop的任务都有一个指定了优先级的模式,为了保证用户界面保持平滑,iOS会提供和用户界面相关任务的优先级,而且当UI很活跃的时候的确会暂停一些别的任务. 一个典型的例子就是当是用UIScrollview滑动的时

iOS自定义转场动画实战讲解

iOS自定义转场动画实战讲解 转场动画这事,说简单也简单,可以通过presentViewController:animated:completion:和dismissViewControllerAnimated:completion:这一组函数以模态视图的方式展现.隐藏视图.如果用到了navigationController,还可以调用pushViewController:animated:和popViewController这一组函数将新的视图控制器压栈.弹栈. 下图中所有转场动画都是自定义的

【iOS开发】---- UIView动画

iOS 动画UIView动画 原文:http://www.cocoachina.com/bbs/read.php?tid=110168 1.概述 UIKit直接将动画集成到UIView类中,实现简单动画的创建过程.UIView类定义了几个内在支持动画的属性声明,当这些属性发生改变时,视图为其变化过程提供内建的动画支持. 执行动画所需要的工作由UIView类自动完成,但仍要在希望执行动画时通知视图,为此需要将改变属性的代码包装到一个代码块中. 2.UIView动画具体创建方法 - (void)bu

IOS开发系列 --- 核心动画

原始地址:http://www.cnblogs.com/kenshincui/p/3972100.html 概览 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌.在这里你可以看到iOS中如何使用图层精简非交互式绘图,如何通过核心动画创建基础动画.关键帧动画.动画组.转场动画,如何通过UIView的装饰方法对这些动画操作进行简化等.在今天的文章里您可以看到动画操作在iOS中是如何简单和高效,很多原来想做但是苦于没有思路的动画在iOS中将变得越发简

iOS开发之核心动画(Core Animation)

1.概述 Core Animation是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍,使用它需要先添加QuartzCore.framework和引入对应的框架<QuartzCore/QuartzCore.h>. 特别注意的是核心动画的动画效果只是“假象”,产生动画的那个view实际上并未发生任何变化. 开发步骤: 第一步:初始化一个动画对象(CAAnimation)并设置一些动画相关属性. 第二步:添加动画对象到层(CALayer)中,开始执行动画. CAL

IOS视图缩放显示动画效果

效果:视图从大--小缩放显示/小--大 (只是比例问题) 方法1.直接show出view的时候:把下面的这段代码加到viewController或者view出现的时候就OK self.view.transform = CGAffineTransformMakeScale(1.0f, 1.0f);//将要显示的view按照正常比例显示出来  [UIView beginAnimations:nil context:UIGraphicsGetCurrentContext()];  [UIView se

IOS学习02简单动画

IOS学习第二天,今天做一个简单的动画的小程序! 程序截图如下: 1 这个程序,当点击屏幕左下方4个方向按键的时候,上面的图片就会跟着自动上下左右移动. 2 当点击屏幕右边变大和变小按键时候,上面的图片就会跟着变大和变小. 二 功能就说到这里,下面开始界面和代码说明. 2.1 首先还是先创建项目和设计界面,当然还要把相对应的图片导入项目images.xcassets下.截图如下: 2.2 界面图片和方向键,在这里我都是用Button做的,当然,用别的也可以. 2.3 界面上所以可以点击的按键,我