Creating Custom UIViewController Transitions

参考www.raywenderlich.com教程
https://www.raywenderlich.com/110536/custom-uiviewcontroller-transitions

本文章基于object c语言,xcode 8.2.1,iOS 8.0

iOS内建了很多控制切换动画,但是如果自己学会定制动画效果,Coding会变得更有意思。好的控制器切换动画会提升用户使用体验,让你的APP更加精致。编写控制器切换动画的代码,其实要比想象的简单。

本篇文章会在我最近写一个App上拿出部分功能进行实践,因为参考的
raywenderlich手册是swift版本的,所以写这个文章的目的是改写出一份OC版本的。如果你看完本篇,会学会下面技术:

  • How the transitioning API is structured.
  • How to present and dismiss view controllers using custom transitions.
  • How to build interactive transitions.

开始

咱们目标要编写一个单页面App,在导航条左侧添加一个选项按钮,点击选项按钮,从屏幕下方向上垂直滑动选项页面(raywenderlich教程效果会很多,本教程是编写便coding的,所以会根据实际过程调整)。

(这里完成后,贴出两个页面截图)

探索View Controller Transitioning API

Transitioning API不是一个具体对象,而是大量的协议。到这节最后,你会学会每个协议的作用和他们在一起使用方法。下面的图显示API中的主要角色:

parts.002.jpeg

角色介绍

虽然上面的图看起来复杂,实际上他们之间的协作非常清晰。

Transitioning Delegate

每一个view controller可以添加一个transitioningDelegate方法。它是遵守实现自UIViewControllerTransitioningDelegate协议,

Whenever you present or dismiss a view controller, UIKit queries the transitioning delegate for the animation controller to use. Setting the view controller’s transitioningDelegate to an instance of your custom class lets you return your own, custom animation controllers instead of the default ones.

不管是显示还是隐藏一个view controller,UIKit会取查询transitioning delegate,提供给animation controller使用。

Animation Controller

这个对象实现了UIViewControllerAnimatedTransitioning协议,它负责执行切换动画。

Transitioning Context

context object实现UIViewControllerContextTransitioning协议,它是切换过程中关键角色:它囊括了所有view controller参与切换的信息。

你不用自己去实现这个协议。在发生切换动画,animation controller就会收到UIKit发送的包含全部信息的context object。

The Transitioning Process

  • 你触发了页面转换
  • UIKit查找目标view controller是否transitioning delegate方法,如果没有,UIKIt使用内建默认的转换方法。
  • UIKit然后会在transitioning delegate查找是否有animation controller,通过 animationControllerForPresentedController(_:presentingController:sourceController:)这个方法. 如果没有找到animation controller那么转换方法使用默认动画。
  • 如果有有效的animation controller,UIKit构建transitioning context对象。
  • UIKit之后会查询动画时长,使用animation controller中的transitionDuration(_:) 方法。
  • 再然后UIKit调用animation controller中的animateTransition(_:) 方法执行转换。
  • 最后,animation controller调用transitioning context中completeTransition(_:) 方法,提示动画完成。

Creating a Custom Presentation Transition

是时候,把新知识实践一下了,你的目标是实现下面动画:

When the user taps a card, it flips to reveal the second view scaled down to the size of the card.
Following the flip, the view scales to fill the whole screen.

Creating the Animator

你马上开始创建一个animation controller。

通过菜单上选择File\New\File…,选择iOS\Source\Cocoa Touch Class,点击next。设置名字为FlipPresentAnimationController,设置继承自NSObject,并且语言为Object c。点击下一步选择文字存储位置。然后点击Create按钮创建文件。

Animation controllers need to conform to the UIViewControllerAnimatedTransitioning protocol. Open FlipPresentAnimationController.swift, and update the class declaration accordingly:

Animation controllers需要遵守UIViewControllerAnimatedTransitioning协议。打开FlipPresentAnimationController.h文件更新类的协议声明:

#import <UIKit/UIKit.h>
@interface FlipPresentAnimationController : NSObject<UIViewControllerAnimatedTransitioning>

@end

Note that you may receive compiler errors due to missing methods; don’t panic – you’re about to fix these.

注释:在FlipPresentAnimationController.m收到missing methods提示,先别急马上就会添加。

staring-at-pc-462x320.gif

You’re going to use the frame of the tapped card as a starting point for the animation. Inside the body of the class, add the following new variable to hold this value:

你将会用点击主view作为动画的开始点。在类中添加下面的变量用以保存这个值:

In order to meet the requirements for the UIViewControllerAnimatedTransitioning, you’ll need to add two methods to the class.
Add the method below inside the class body:
为了满足UIViewControllerAnimatedTransitioning协议要求,你需要添加两个方法。
在类的体内添加下面的方法:

    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext 
    { 
        return 0.25; 
    } 

As the name suggests, this method specifies the duration of your transition. Setting it to two seconds will prove useful during development, as it leaves enough time to observe the animation.
Now add the following method stub to the class:
通过名字可以知道这个方法指定了专场时间为2秒。

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext 
{ 
    
} 

This protocol method is where you’ll implement the transition animation itself. Start off by inserting the following lines at the top of the method:

这个方法是实现

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext 
{ 
    // 1. Get controllers from transition context 
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; 
  
    // 2. Set init frame for toVC 
    CGRect screenBounds = [[UIScreen mainScreen] bounds]; 
    CGRect finalFrame = [transitionContext finalFrameForViewController:toVC]; 
    toVC.view.frame = CGRectOffset(finalFrame, 0, screenBounds.size.height); 
  
    // 3. Add toVC's view to containerView 
    UIView *containerView = [transitionContext containerView]; 
    [containerView addSubview:toVC.view]; 
  
    // 4. Do animate now 
    NSTimeInterval duration = [self transitionDuration:transitionContext]; 
    [UIView animateWithDuration:duration 
                          delay:0.0 
         usingSpringWithDamping:0.6 
          initialSpringVelocity:0.0 
                        options:UIViewAnimationOptionCurveLinear 
                     animations:^{ 
                         toVC.view.frame = finalFrame; 
                     } completion:^(BOOL finished) { 
                         // 5. Tell context that we completed. 
                         [transitionContext completeTransition:YES]; 
                     }]; 
} 

Here’s what’s going on above:
这里是上面代码的解释:

  1. transitioning context将提供view controller和参与专场的view。你使用恰当的key在获得他们。
  1. 你下一步指定目标视图的开始帧和结束帧。这个例子转场从卡片帧扩展到整个屏幕,并把快照放到一个不重要的view里面;

  2. UIView对目标视同进行快照,这使您可以将视图与它的层次结构一起动画化。快照那一帧作为卡片帧的开始。你也可以修改成适合卡片的角的弧度
    Continue by adding the following lines to the method body:

继续在方法里添加下面的代码:

A new player appears: the container view. Think of this as the dance floor upon which your transition shakes its stuff. The container view already contains the “from” view, but it’s your responsibility to add the “to” view.
You also add the snapshot view to the container and hide the real view for now. The completed animation will rotate the snapshot out of view and hide it from the user.
出现了一个新角色:container view。container view包括开始的view,你现在的责任是添加目标view。

你也添加目标view的快照给了container,并且把目标view给隐藏了。

Note: Don’t let AnimationHelper confuse you. It’s a small utility class, responsible for adding perspective and rotation transforms to your views. Feel free to have a look at the implementation.

注释:不要让AnimationHelper让你迷惑了。他是一个工具类,负责把view添加透视和旋转变换。在写这块代码再仔细说明下。

At this point, you have all the necessary parts in place to perform the animation. Add the final lines of code to the bottom of the method:

在现在,你已经添加了执行动画的所有必要代码。

Taking each commented section in turn:
我们逐一按顺序解释上面的代码:

First, you specify the duration of the animation. Notice the use of the transitionDuration(_:) method, implemented at the top of this class. You need the duration of your animations to match up with the duration you’ve declared for the whole transition so UIKit can keep things in sync.

  1. 首先,你指定了动画的时长。注意我们使用的transitionDuration方法,已经在上面实现了。你要确保动画的时长相适应之前声明的整个转场的时长,UIKit可以保证整个是同步的。

You start by rotating the “from” view halfway around its y-axis to hide it from view.

  1. 首先,旋转“从”视图到它的y轴的一半,从视图中隐藏它。

Next, you reveal the snapshot using the same technique.

  1. 接下来用同样的技术让快照进行了旋转。

Then you set the frame of the snapshot to fill the screen.

  1. 然后你让快照的帧填充整个屏幕。

Finally, it’s safe to reveal the real “to” view. You remove the snapshot since it’s no longer useful. Then you rotate the “from” view back in place; otherwise, it would hidden when transitioning back. Calling completeTransition informs the transitioning context that the animation is complete. UIKit will ensure the final state is consistent and remove the “from” view from the container.

  1. 最终,

You’re now ready to use your animation controller!

马上你就要使用定制的animation controller!

Wiring Up the Animator

Open CardViewController.swift and declare the following property for the class:
打开CardViewController添加下面的属性:

UIKit expects a transitioning delegate to vend the animation controller for a transition. To do this, you must first provide an object which conforms to UIViewControllerTransitioningDelegate.
UIKit

In this example, CardViewController will act as a transitioning delegate. Add the following class extension to the bottom of the source file to make this class conform to UIViewControllerTransitioningDelegate:
在这个示例中,CardViewController将作为transitioning delegate。

在类中添加下面的方法

Here you return your custom animation controller instance. The method will also ensure the transition starts from the correct frame.
这里返回你定义的animation controller的实例。这个方法也将确保从正确的帧开始专场动画。

Your final step is to mark CardViewController as the transitioning delegate. View controllers have a transitioningDelegate property, which UIKit will query to see if it shoulld use a custom transition.
最后一步,标记CardViewController作为transitioning delegate。View controllers有transitioningDelegate属性,如果使用了自定义transition,UIKit将会查询到。

It’s important to note that it is the view controller being presented that needs a transitioning delegate, not the view controller doing the presenting!
很重要一点,view controller展现需要一个 transitioning delegate,并不是view controller负责的展现!

Build and run your project; tap on a card and you should see the following:
编译运行项目;点击卡片你可以看到下面的样子:

There you have it – your first custom transition. But presenting your new view is only half the solution: you need to dismiss it in an equally showy manner!
你完成了第一个自定义transition。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • ViewsBecause view objects are the main way your applicati...
    梁光飞阅读 587评论 0 0
  • /* UIViewController is a generic controller base class th...
    DanDanC阅读 1,789评论 0 2
  • 更好的阅读体验,请到个人博客阅读: iOS中的系统转场 请忽略标题,😂,本文记录的是对下图所示的Kind, Pre...
    CaryaLiu阅读 2,338评论 0 1
  • 沉睡了一个冬季的大地醒来了 拘谨的小鸟飞出了温暖的巢穴 天空如同湛蓝 心情放飞在蓝天
    帅帅不过三千年阅读 308评论 0 0
  • 说服别人支持你,不一定要证明比别人都优秀,而是要让别人觉得,因为有你,他们才变得更优秀、更有成就感。
    f6ba252fea17阅读 75评论 0 0