您的位置:首页 > 移动开发 > IOS开发

iOS进阶之旅-可交互式转场动画

2016-02-28 18:18 609 查看
iOS7苹果为开发者引入了一系列新的API。其中一个尤为出色的API是一个新的视图控制器转换API。有趣的是,对于这个API苹果大量使用了协议,而不是具体的对象。虽然有点奇怪,但这种定义方式为我们提供了极大的灵活性,在深入探讨这个API之前,让我们看一下另一个iOS7的新特性。注意导航控制器在iOS7中默认行为的改变,在导航控制器不同视图切换之间的动画在iOS7之后现在略有不懂,它是可交互的,即我们俗称的右滑返回。所谓转场动画就是视图切换动画,交互式就是动画切换的进度依赖于用户的操作。本文将围绕UINavigationController的push、pop操作切换控制器进行讨论。但,苹果所提供的API是同样支持控制器的present、dismiss操作切换控制器与UITabbarViewController切换控制器的,其实现原理基本一致,在次就不多赘述了,希望和我一样在探索进阶之路的,如果看到这篇博客后能对你起到帮助,能自己去实现present、dismiss(实现UIViewControllerTransitioningDelegate)与UITabbarViewController(实现UITabBarControllerDelegate)的转场切换。废话不多说,直入主题,我们今天做一个非常简单的demo,首先是一个不可交互的转场动画,push为渐入效果,pop为从屏幕大小缩小为CGZero。push、pop操作么,肯定要与navigation controller扯上点关系了,我们说了新的API是代理,让我们来一起看一下UINavigationControllerDelegate中的一个方法- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController*)
navigationController
                                  animationControllerForOperation:(UINavigationControllerOperation)
operation
                                               fromViewController:(UIViewController *)
fromVC
                                                 toViewController:(UIViewController *)
toVC
官方的描述:Implement this delegatemethod when you want to provide a custom animated transition between view controllers as they are added to or removed from the navigation stack. The object you return should be capable of configuring and performing noninteractive animations for the specifiedview controllers for the specified type of operation over a fixed period of time.如果你想提供一个在视图被加入或从导航栈移除时提供自定义的动画转换视图的导航控制器,执行这个代理方法。该方法的返回对象能够帮助你配置视图动画和动画的持续时间。说的够明白了吧。我们把目光聚集到返回值id<UIViewControllerAnimatedTransitioning>,一个实现UIViewControllerAnimatedTransitioning协议的代理,是的又是代理而不是对象。我们看一下两个声明为required的方法声明。- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)
transitionContext
官方描述:UIKit calls this method to obtain the timing information for your animations. The value you provide should be the same value that you use when configuringthe animations in yo4000ur animateTransition: method. UIKit uses the value to synchronize the actions of other objects that might be involved in the transition. For example, a navigation controller uses the value to synchronize changes to the navigation bar.UIKit调用此方法来获得你的动画的时间信息。你配置的值应该与你在animatetransition:方法中设置的动画时间是一致的。UIKit使用该值来同步其他对象可能涉及过渡的行动。例如,导航控制器使用该值来对导航栏进行同步更改。简而言之,就是在这个方法里设置动画的时间- (void)animateTransition:(id<UIViewControllerContextTransitioning>)
transitionContext
官方描述:
UIKit calls this method when presenting or dismissing a view controller. Use this method to configure the animations associated with your custom transition. You can use view-basedanimations or Core Animation to configure your animations.UIKit时调用此方法在将要显示或退出一个视图控制器时。使用此方法来配置与自定义转换相关联的动画。你可以使用基于视图的动画或CoreAnimation来配置你的动画。简而言之,就是把你想要的动画放到这里。
看到这里聪明的你脑袋里肯定已经有思路了,我们将一个类声明为UINavigationController的代理,在其中实现
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController*)
navigationController
                                  animationControllerForOperation:(UINavigationControllerOperation)
operation
                                               fromViewController:(UIViewController *)
fromVC
                                                 toViewController:(UIViewController *)
toVC
然后创建一个实现UIViewControllerContextTransitioning协议的对象,在该方法中返回。是的,你说的一点也不错。让我们来完成这个demo吧。首先创建一个UICustomPushAnimation 实现UIViewControllerContextTransitioning协议,在这个类中我们处理关于push的转场动画.h文件#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>@interface BWCustomPushAnimation :NSObject<UIViewControllerAnimatedTransitioning>@end.m文件#import "BWCustomPushAnimation.h"@implementation BWCustomPushAnimation-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{    return3.0;}-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{//    目的ViewController    UIViewController * toViewController = [transitionContextviewControllerForKey:UITransitionContextToViewControllerKey];//    起始ViewController    UIViewController * fromViewController = [transitionContextviewControllerForKey:UITransitionContextFromViewControllerKey];        UIView * toView = toViewController.view;    UIView * fromView = fromViewController.view;    //  截图并添加    UIView * snapOfToView = [toViewsnapshotViewAfterScreenUpdates:YES];    [transitionContext.containerViewaddSubview:snapOfToView];        UIView * snapOfFromView = [fromViewsnapshotViewAfterScreenUpdates:YES];    [transitionContext.containerViewaddSubview:snapOfFromView];       //    动画实现    fromViewController.view.alpha =1;    toViewController.view.alpha =1;    [UIViewanimateWithDuration:[selftransitionDuration:transitionContext] animations:^{        snapOfFromView.alpha =0;    } completion:^(BOOL finished) {//        动画完成后一定要移除        [snapOfFromView removeFromSuperview];        [snapOfToView removeFromSuperview];        //        设置transitionContext通知系统动画执行完毕        [[transitionContext containerView]addSubview:toView];        [transitionContext completeTransition:![transitionContexttransitionWasCancelled]];    }];}@end创建一个BWCustomPopAnimation 实现UIViewControllerContextTransitioning协议,在这个类中我们处理关于pop的转场动画.h文件#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>@interface BWCustomPopAnimation :NSObject<UIViewControllerAnimatedTransitioning>@end.m文件#import "BWCustomPopAnimation.h"@implementation BWCustomPopAnimation- (NSTimeInterval)transitionDuration:(nullableid <UIViewControllerContextTransitioning>)transitionContext{    return3.0;}// This method can only  be a nop if the transition is interactive and not a percentDriven interactive transition.- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{    UIViewController * toVC = [transitionContextviewControllerForKey:UITransitionContextToViewControllerKey];    UIViewController * fromVC = [transitionContextviewControllerForKey:UITransitionContextFromViewControllerKey];        UIView * toView = toVC.view;    UIView * fromView = fromVC.view;        UIView * snapOfToView = [toViewsnapshotViewAfterScreenUpdates:YES];    [transitionContext.containerViewaddSubview:snapOfToView];    UIView * snapOfFromView = [fromViewsnapshotViewAfterScreenUpdates:YES];    [transitionContext.containerViewaddSubview:snapOfFromView];    //    [snapOfToView setFrame:CGRectMake([UIScreen mainScreen].bounds.size.width/2.0, [UIScreen mainScreen].bounds.size.height/2.0, 10, 10)];    [snapOfFromView setFrame:[UIScreenmainScreen].bounds];    //    snapOfToView.layer.transform = catran    [UIViewanimateWithDuration:[selftransitionDuration:transitionContext] animations:^{        [snapOfFromView setFrame:CGRectMake([UIScreenmainScreen].bounds.size.width/2.0, [UIScreenmainScreen].bounds.size.height/2.0,10,10)];//        [snapOfToView setFrame:[UIScreen mainScreen].bounds];    }completion:^(BOOL finished) {        [snapOfFromView removeFromSuperview];        [snapOfToView removeFromSuperview];                [[transitionContext containerView]addSubview:toView];        [transitionContext completeTransition:![transitionContexttransitionWasCancelled]];    }];    }@end在Appdelegate中创建fromVC- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {           self.window = [[UIWindowalloc]initWithFrame:[UIScreenmainScreen].bounds];        BBTFromVC * fromVC = [[BBTFromVCalloc]init];    [self.windowmakeKeyAndVisible];        UINavigationController * nav = [[UINavigationControlleralloc]initWithRootViewController:fromVC];    nav.interactivePopGestureRecognizer.enabled =YES;    self.window.rootViewController = nav;      returnYES;}在fromVC中实现UINavigationController的代理方法- (nullableid <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController                                            animationControllerForOperation:(UINavigationControllerOperation)operation                                                         fromViewController:(UIViewController *)fromVC                                                           toViewController:(UIViewController *)toVC{    if (operation ==UINavigationControllerOperationPush) {        return self.customPush;    }elseif (operation ==UINavigationControllerOperationPop){        return self.custonPop;    }    returnnil;}在viewDidLoad中声明代理 self.navigationController.delegate= self;customPush与customPop的懒加载-(BWCustomPushAnimation *)customPush{    if (_customPush ==nil) {        _customPush = [[BWCustomPushAnimationalloc]init];    }    return_customPush;}-(BWCustomPopAnimation *)custonPop{    if (_custonPop ==nil) {        _custonPop = [[BWCustomPopAnimationalloc]init];    }    return_custonPop;}至此,你发现我们所需要的功能完美实现了,但真的是完美么,比如右滑返回呢,你会发现右滑返回失效了,专注用户体验三十年的我们,这种事情当然不能忍。我们需要加上右滑返回,而这就是我们所说的可交互式转场动画。(可交换式转场动画并不等于右滑返回,我们前面说了,苹果为我们提供了极大的灵活性,可交换式转场动画比你想象的强大,我们只是以可交互式转场动画作为右划返回的一种实现方式)。一起看一下系统API,同样是在UINavigationControllerDelegaate中- (nullableid <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController                          interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) 官方文档:Implement this delegate method when you want to provide a custom, interactive transition between view controllers as they are added to or removed from the navigation stack. 当你想提供一个自定义的、交互的视图控制器之间的过渡,当他们被添加到或从导航堆栈中移除时,执行此委托方法。该方法的返回值是一个实现了UIViewControllerInteractiveTransitioning协议的对象,这个对象不需要我们自己实现,系统的UIPercentDrivenInteractiveTransition对其进行了封装,我们只需返回一个该类的实例就可以。一起来看一下这个类官方文档:A percent-driven interactive transition object drives the custom animation between the disappearance of one view controller and the appearance of another. It relies on a transition animatordelegate—a custom object that adopts the UIViewControllerAnimatorTransitioning protocol—to set up and perform the animations.To use this concrete class, return an instance of it from your view controller delegate when asked for an interactive transition controller. As user events arrive that would affect theprogress of a transition, call the updateInteractiveTransition:, cancelInteractiveTransition, and finishInteractiveTransition methods to reflect the current progress. For example, you might call these methods from a gesture recognizer to reflect how much ofthe gesture is completed.一个UIPercentDrivenInteractiveTransition交互式转场对象驱动在视图切换时自定义动画的展现。它依赖于一个过渡动画代理--一个自定义的遵循UIViewControllerAnimatorTransitioning 协议的对象,以此来设置和展现动画。要使用这个具体的类,当被要求为一个交互式的转换控制器时,返回一个实例控制器委托的实例。当用户到达的事件会影响过渡进程,调用updateinteractivetransition:,cancelinteractivetransition,和finishinteractivetransition方法反映当前进展。例如,你可能这些方法从一个手势识别来电反映多少的手势完成。一起来看一下这三个过渡进程的方法[code]- (void)updateInteractiveTransition:(CGFloat)
percentComplete
官方文档:While tracking user events, your code should call this method regularly to update the current progress toward completing the transition. If, during tracking, the interactions cross a threshold thatyou consider signifies the completion or cancellation of the transition, stop tracking events and call the finishInteractiveTransition or cancelInteractiveTransition method.在跟踪用户事件时,您的代码应该定期调用此方法来更新当前的进展,以完成该转换。如果,在跟踪过程中,相互作用的跨出门槛,你认为标志着过渡完成或取消,停止跟踪事件并调用finishinteractivetransition或cancelinteractivetransition方法- (void)cancelInteractiveTransitionWhile tracking user interactions, your gesture recognizer or event-handling code would call this method when interactions suggest that the user wants to cancel or abort the view controller transition.For example, if the user reverses the swipe direction and then touch events end, suggesting that the user decided against the transition, you would call this method.在跟踪用户的交互,你的手势识别或事件处理代码将调用此方法时的相互作用表明,用户想取消或中止的视图控制器的过渡。例如,如果用户反转了滑动方向,然后触摸事件结束,提示用户决定不改变,您将调用此方法。- (void)finishInteractiveTransitionWhile tracking user interactions, your gesture recognizer or event-handling code should call this methods when the interactions suggest that the transition is now complete. For example, if the user swipesa finger, and the touch events indicate that the swipe distance crossed the threshold needed to complete the gesture, call this method when the corresponding touch events end to let the system know that it can now complete the transition.在跟踪用户的交互,你的手势识别或事件处理代码应调用此方法时的相互作用表明,过渡已经完成。例如,如果用户使用手指和触摸事件表明,刷卡距离交叉所需要完成的动作阈值时,调用此方法时,相应的触摸事件最终让系统知道它现在可以完成转型。那么现在,思路清晰了。1。在FromVC中实现UINavigationController的代理方法 2.为控制器注册一个手势 3、在手势的handler方法中,根据手势来调用以上三个方法,实现交互式控制动画进程在FromVC中实现代理方法- (nullableid <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController                                   interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController{    returnself.interactionController;}self.interactionController的声明@property(nonatomic,strong)UIPercentDrivenInteractiveTransition * interactionController;注册手势UIPanGestureRecognizer * panRecognizer = [[UIPanGestureRecognizeralloc]initWithTarget:selfaction:@selector(didClickPanGestureRecognizer:)];    [self.navigationController.viewaddGestureRecognizer:panRecognizer];手势handler实现并控制动画进程-(void)didClickPanGestureRecognizer:(UIPanGestureRecognizer *)recognizer{    UIView * view =self.view;    if (recognizer.state ==UIGestureRecognizerStateBegan) {        if (self.navigationController.viewControllers.count== 2) {            self.interactionController = [[UIPercentDrivenInteractiveTransitionalloc]init];            [self.navigationControllerpopViewControllerAnimated:YES];        }    }elseif (recognizer.state ==UIGestureRecognizerStateChanged){        CGPoint translation = [recognizertranslationInView:view];        CGFloat distance = translation.x/CGRectGetWidth(view.bounds);        if (distance >0) {            [self.interactionControllerupdateInteractiveTransition:distance];        }    }elseif (recognizer.state ==UIGestureRecognizerStateEnded){        CGPoint translation = [recognizertranslationInView:view];        CGFloat distance =fabs(translation.x/CGRectGetWidth(view.bounds));                if (distance >0.5) {            [self.interactionControllerfinishInteractiveTransition];        }else{            [self.interactionControllercancelInteractiveTransition];        }//       self.interactionController一定要设置为nil,否则你可能在你不想返回的时候返回一个interactionController        self.interactionController =nil;    }    }至此,我们实现了UINavigationController的交互式转场动画,学海无涯,如果这里有任何错误,希望能够给予指正,以便我的进步,谢谢。邮箱 angxian66@163.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息