参考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中的主要角色:
角色介绍
虽然上面的图看起来复杂,实际上他们之间的协作非常清晰。
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提示,先别急马上就会添加。
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:
这里是上面代码的解释:
- transitioning context将提供view controller和参与专场的view。你使用恰当的key在获得他们。
你下一步指定目标视图的开始帧和结束帧。这个例子转场从卡片帧扩展到整个屏幕,并把快照放到一个不重要的view里面;
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.
- 首先,你指定了动画的时长。注意我们使用的transitionDuration方法,已经在上面实现了。你要确保动画的时长相适应之前声明的整个转场的时长,UIKit可以保证整个是同步的。
You start by rotating the “from” view halfway around its y-axis to hide it from view.
- 首先,旋转“从”视图到它的y轴的一半,从视图中隐藏它。
Next, you reveal the snapshot using the same technique.
- 接下来用同样的技术让快照进行了旋转。
Then you set the frame of the snapshot to fill the screen.
- 然后你让快照的帧填充整个屏幕。
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.
- 最终,
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。