`

定制View Controller的切换特效

 
阅读更多
本文译自objc.io的文章:http://www.objc.io/issue-5/view-controller-transitions.html

定制动画效果
iOS7中最让我激动的特性之一就是提供了新的API来支持做定制View Contrioller之间的转换特效。iOS7发布之前,我自己写过一些View Controller之间转换动画,这是一个比较头疼的过程,而且这种做法支持的并不完整,尤其是如果你想让这个转换动画有交互式的效果就更难了。

在继续阅读之前,我需要先声明一下:objc.io的文章通常是介绍各种iOS开发的最佳实践,但是,这个API是新近才发布的,目前还没有所谓的最佳实践,通常来说,开发者需要探索几个月才能知道关于新的API的最佳实践。因此请将本文看做对一个新API的探索,而非关于这个新API的最佳实践介绍。如果您有更好的关于这个API的实践,请不吝赐教,我们会把您的实践更新到这篇文章中。

在开始研究新的API之间,我们先来看看在iOS7中导航视图控制器之间的默认的行为发生了那些改变:在导航视图控制器的容器中,View Controller之间的转换动画有些小改动,这些改动让View Controller之间的切换有了交互的效果,比方说你想要弹出一个View Controller,可以把替换View Controller从屏幕左边平移过来,也可以把当前的View Controller拉到屏幕右边。

接下来,我们来看看新的API,有个发现个人觉得很有趣,这部分API使用了大量的protocols而非具体的对象,这初看起来有点诡异。但是细想起来,我个人更喜欢这样的API设计,因为这给了开发者更大的灵活性。下面,让我们来做件简单的事情:为把一个View Controller推入到导航视图控制器的容器中的操作,实现一个动画效果(本实例代码已托管在Github上:https://github.com/objcio/issue5-view-controller-transitions)。为了完成这个任务,需要实现UINavigationControllerDelegate类中的新方法:
- (id<UIViewControllerAnimatedTransitioning>)
                   navigationController:(UINavigationController *)navigationController
        animationControllerForOperation:(UINavigationControllerOperation)operation
                     fromViewController:(UIViewController*)fromVC
                       toViewController:(UIViewController*)toVC
{
    if (operation == UINavigationControllerOperationPush) {
        return self.animator;
    }
    return nil;
}


从上面的代码可以看出,我们可以根据不同的操作(压入或弹出)返回不同的动画效果,如果想在多个操作之间共享动画效果,可以把动画效果设置为类的一个属性,然后再多个操作中共享。或者我们可以为每个操作都创建一个专属的动画效果对象,这儿存在很大的灵活性,就看开发者想怎么玩了。

为了执行动画效果,我们需要创建一个实现了UIViewControllerContextTransitioning协议的对象:
@interface Animator : NSObject <UIViewControllerAnimatedTransitioning>

@end

这个协议要求我们实现2个方法,一个定义了动画的持续时间:
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
    return 0.25;
}

另一个定义了整个动画的执行效果:
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    [[transitionContext containerView] addSubview:toViewController.view];
    toViewController.view.alpha = 0;
    
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        fromViewController.view.transform = CGAffineTransformMakeScale(0.1, 0.1);
        toViewController.view.alpha = 1;
    } completion:^(BOOL finished) {
        fromViewController.view.transform = CGAffineTransformIdentity;
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        
    }];

}

从上面的例子中,你可以看到应该怎么使用这个协议:这个方法中通过接受一个类型为id<UIViewControllerContextTransitioning>的参数,来获取切换的上下文信息。值得注意的是,执行完动画效果之后,我们需要调用TransitionContext的completeTransition方法并更新View Controller的状态,剩下的代码和iOS7之前的一样了,我们从上下文信息中得到了需要做切换的2个View Controller,然后使用最简单的UIView动画效果,这就是实现一个切换View Controller的定制动画效果所需的全部代码,现在我们有了一个定制的切换动画效果了。

注意,这儿只是为Push操作实现了定制动画效果,对于Pop操作,还是会使用默认的滑动动画效果,另外,上面我们的实现中并没有交互效果,下面我们就来看看如何定制一个交互式的切换效果

交互式动画效果
制作一个交互式的动画效果非常简单,我们只需要覆盖另一个UINavigationControllerDelegate的方法即可:
- (id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController*)navigationController
                          interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>)animationController
{
    return self.interactionController;
}

注意,在非交互式动画效果中,该方法返回nil。

这儿返回的interactionController属性是UIPercentDrivenInteractionTransition类的一个实例,开发者甚至都不需要进一步的配置或设置就可工作,我们创建了一个平移手势识别,下面就是处理该手势的代码:
if (panGestureRecognizer.state == UIGestureRecognizerStateBegan) {
    if (location.x >  CGRectGetMidX(view.bounds)) {
        navigationControllerDelegate.interactionController = [[UIPercentDrivenInteractiveTransition alloc] init];
        [self performSegueWithIdentifier:PushSegueIdentifier sender:self];
    }
} 

只有用户的手势操作发生在屏幕右半部分的时候,我们把下一次动画效果设置为交互式的(通过设置interactionController属性),然后执行方法performSegueWithIdentifier做视图切换(如果你不是使用的storyboards,可以使用导航视图控制的pushViewController方法),为了驱动视图切换的动画效果,我们需要调用interactionController属性的方法:
else if (panGestureRecognizer.state == UIGestureRecognizerStateChanged) {
    CGFloat d = (translation.x / CGRectGetWidth(view.bounds)) * -1;
    [interactionController updateInteractiveTransition:d];
} 

该方法会根据用户手势的平移距离计算一个百分比,表示动画效果已运行的百分比,最酷的是,interactionController会和动画视图一起协作,我们只使用了简单的UIView的动画效果,但是interactionController却控制了动画的执行进度,开发者都不需要把interactionController和动画Controller关联起来,所有的一切以一种离散的方式配合运行。

最后,我们需要根据用户手势的停止状态来判断该操作是结束了还是取消了?我们需要根据不同的结果去调用interactionController中对应的方法:
else if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) {
    if ([panGestureRecognizer velocityInView:view].x < 0) {
        [interactionController finishInteractiveTransition];
    } else {
        [interactionController cancelInteractiveTransition];
    }
    navigationControllerDelegate.interactionController = nil;
}


注意,当切换完成或者取消的时候,记得把interactionController设置为nil,这样可以防止在下一次交互式切换时受到上一次的interactionController属性状态的影响。

现在我们已经完成了一个功能完备的交互式View Controlle切换动画效果,就只使用了简单的手势识别和UIKit提供的一个类,用几行代码就可以达到这样的效果。对于大部分的应用场景,你读到这儿就够用了,剩下的事就是思考如何使用上面提到的方法达到你想要的效果。但是,如果你想更深入的定制切换动画的效果,我可以接着往下看。

###使用GPUImage定制动画
下面我们就来看看如何真正的,彻底的定制动画效果,不使用UIView动画,和Core Animation,完全由自己完成所有的动画效果,Letterpress-style(作者的一个项目)中,刚开始,我将尝试使用Core Image来做动画效果,但是在我的iPhone 4上,动画效果的渲染最高只能达到9帧/秒,离我想要的60帧/秒差得很远。

iOS7引入GPUImage之后,开发者可以非常容易的实现一个效果非常好的动画,这儿我想构建一个2个视图控制器相互模糊,消融的动画效果。实现方法就是为2个视图分别照一个快照,然后在快照上使用GPUImage的过率器来达到动画的效果。

首先,我们先写一个 类,这个类实现了UIViewControllerAnimatedTransitioning和UIViewControllerInteractiveTransitioning协议。
@interface GPUImageAnimator : NSObject
  <UIViewControllerAnimatedTransitioning,
   UIViewControllerInteractiveTransitioning>

@property (nonatomic) BOOL interactive;
@property (nonatomic) CGFloat progress;

- (void)finishInteractiveTransition;
- (void)cancelInteractiveTransition;

@end


为了加速动画的运行,我们可以把图片一次加载到GPU中,然后所有的处理,画图都直接在GPU上执行,不需要再传送到CPU处理(这减少了数据传送时间),通过使用GPUImageView,我们就可以直接使用OpenGL画图(不需要深入到OpenGL底层去编码,我们只需要在高抽象层级编写代码)。

创建过滤器链也非常的直观,我们可以直接在样例代码的setup方法中看到如何构造它。比较难的是如何动态修改过滤器的配置,使用GPUImage,我们无法自动获得动画效果,因此我们需要每渲染一帧就更新一下过滤器。我们可以使用CADisplayLink类来完成这个工作:
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(frame:)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];


在frame方法中,我们可以根据当前的进度更新系统的状态,包括所有的过滤器状态:
- (void)frame:(CADisplayLink*)link
{
    self.progress = MAX(0, MIN((link.timestamp - self.startTime) / duration, 1));
    self.blend.mix = self.progress;
    self.sourcePixellateFilter.fractionalWidthOfAPixel = self.progress *0.1;
    self.targetPixellateFilter.fractionalWidthOfAPixel = (1- self.progress)*0.1;
    [self triggerRenderOfNextFrame];
}

好了,这基本上就是我们所需要知道的所有知识了,为了实现交互式的切换效果,我们需要保证根据手势的执行跟新进度,而非根据时间,剩下的处理代码基本上都一样。

这个功能非常强大,你可以在GPUImage中使用所有已有的过滤器,或者写一个自己的OpenGL着色器达到想要的效果。

结论
本文只探讨了在导航视图容器中的2个视图切换时的动画效果设计,但是这些做法在Tab Bar视图容器,或者你自定义的视图容器中都可以是通用。另外,UICollectionViewController经过扩展甚至支持切换布局时的动画效果了,背后使用的也是同样的机制。这真的太强大了。

在和Orta讨论这个API的时候,他提到他大量的使用了这些机制以创建更轻量的视图,你可以通过创建多个视图控制器,定制视图控制器间的切换效果,在视图控制器之间共享视图的方式来取代在一个视图控制器中维护各种状态。

延伸阅读:
WWDC: Custom Transitions using View Controllers(http://asciiwwdc.com/2013/sessions/218
Custom UIViewController transitions(http://www.teehanlax.com/blog/custom-uiviewcontroller-transitions/
iOS 7: Custom Transitions(http://www.doubleencore.com/2013/09/ios-7-custom-transitions/
Custom View Controller Transitions with Orientation(http://whoisryannystrom.com/2013/10/01/View-Controller-Transition-Orientation/
0
0
分享到:
评论

相关推荐

    IOS应用源码——多种view视图切换效果.zip

    开发者可以通过实现UIViewControllerAnimatedTransitioning和UIViewControllerTransitioningDelegate协议,为视图切换定制个性化的动画效果。 7. **Auto Layout**:为了适配不同尺寸的屏幕,iOS引入了Auto Layout。...

    多种view视图切换效果.zipIOS应用例子源码下载

    本资源“多种view视图切换效果.zip”提供了一个iOS应用的源码示例,旨在帮助学生、个人开发者或公司团队更好地理解和实践视图切换技术。 1. **基础视图切换** - **UIStoryboardSegue**: iOS中的UIStoryboardSegue...

    轻松创建 iOS View Controller 动画过度效果.zip

    这个开源项目“轻松创建 iOS View Controller 动画过度效果”旨在帮助开发者实现自定义的、交互式的控制器转换动画,使得应用程序的界面切换不仅美观,而且能够灵活响应用户的操作。 在iOS中,UIViewController类...

    IOS应用源码Demo-效果不错的view视图滑动切换效果-毕设学习.zip

    本示例源码“效果不错的view视图滑动切换效果”提供了一个展示如何实现动态、交互式的视图切换效果的学习资源,特别适用于iOS毕业设计或论文研究。在这个项目中,开发者可能会学到以下关键知识点: 1. **...

    iphone 各种视图切换效果

    在iOS开发中,iPhone应用程序的用户体验往往离不开各种视图(View)之间的切换效果。这些效果不仅提升了用户界面的美观度,还能提供更好的交互体验。本文将深入探讨“iPhone各种视图切换效果”,并结合源码分析,...

    swift-左右切换controller工具

    在这个场景中,我们讨论的是一个名为"swift-左右切换controller工具"的组件,它主要用于在两个界面之间轻松实现点击按钮的切换效果。这个工具的核心是`BSTwoControllerView`,它解决了在一个控制器(Controller)中...

    iPhone上画面切换特效及代码

    本文将深入探讨如何在iPhone上实现各种画面切换特效,并提供相应的代码示例。 首先,我们要了解iOS中的转场动画主要依赖于`UIViewControllerTransitioningDelegate`和`UIViewControllerAnimatedTransitioning`这两...

    iPhone之手动切换View示例程序

    首先,我们要了解View Controller(UIViewController)的角色。在iOS应用中,UIViewController是用来管理一个或多个View的类。在这个示例中,我们将创建两个UIViewController子类,分别代表两个不同的视图。每个...

    点击切换视图滑动切换视图

    这种技术通常涉及到UITableView、UICollectionView或者通过Container View Controller来实现,旨在提供流畅的用户体验。以下是关于这个主题的详细解释: 1. **UITableView与UICollectionView**: 这两种是iOS中的...

    视图切换的动画效果

    动画效果主要通过`animationControllerForOperation:fromViewController:toViewController:`或`transitionCoordinator`来定制。 1. **UIStoryboardSegue的自定义动画**: `UIStoryboardSegue`提供了`perform:`方法...

    按钮切换两个视图

    在这个例子中,`currentViewController`是用于记录当前显示的视图控制器,`firstViewController`和`secondViewController`则是要切换的两个控制器。 总的来说,"按钮切换两个视图"涉及到UI设计、用户交互、视图控制...

    objectivec view controller跳转

    3. **页面控制器(Page Controller)**:`UIPageViewController`用于实现类似翻书效果的页面切换。每个页面对应一个`UIViewController`实例。 4. **Tab 控制器(Tab Controller)**:`UITabBarController`用于管理...

    第七节 利用ModalViewController切换View.docx

    CustomViewController* controllerB = [[CustomViewController alloc] init]; controllerB.view.backgroundColor = [UIColor redColor]; [controller presentModalViewController:controllerB animated:YES]; ...

    (0043)-iOS/iPhone/iPAD/iPod源代码-视图切换(View Transition)-Ticker View

    本教程主要探讨的是如何在iOS应用中实现一种独特的视图切换效果,即“Ticker View”,它模仿了Flipboard应用中的翻页效果,使得视图能够从上至下或从下至上折叠,这种效果可以用于页面切换、图片切换以及模拟计时器...

    StoryBoard视图切换

    6. **使用PageViewController(UIPageViewController)**:如果需要创建类似电子书或相册的翻页效果,Page View Controller是一个不错的选择。通过`setViewControllers(_:direction:animated:completion:)`方法,...

    滑动切换ViewController

    4. **处理页面切换的回调**:`pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:`是页面切换完成时的回调,你可以在这里进行一些动画效果的调整或者状态更新。 ```swift func ...

    IOS应用源码——一个适用于iPad的UI切换效果.rar

    在iOS应用中,UI切换通常涉及到UIViewController的生命周期管理、Container View Controller的概念、UIStoryboardSegue的使用,以及可能的自定义转场动画。开发者可能会用到苹果提供的UIKit框架,特别是...

    游戏视图控制工具:IKARUS Game View Controller v1.3

    IKARUS: 游戏视图控制器是一个 Unity 系统,它允许用户直接在游戏视图中控制主摄像机和光源,无需切换到播放模式。该系统能够正确预览所有图像效果,帮助用户实现精确的摄像机定位。 IKARUS 系统不是场景视图的替代...

    ios-视图撕裂切换效果.zip

    该项目作为一个开源示例,应该具有清晰的文件结构,遵循MVC(Model-View-Controller)设计模式,便于其他开发者理解和使用。 总的来说,"ios-视图撕裂切换效果.zip"提供了学习和研究自定义视图动画的一个实例,对于...

Global site tag (gtag.js) - Google Analytics