iOS开发中的ViewController转场切换效果实现简介


在iOS7之前,View Controller的切换主要有4种:

  1. Push/Pop,NavigationViewController
  2. Present and dismis Modal
  3. UITabBarController
  4. addChildViewController(一般用于自定义的继承于 UIViewController 的容器子类)

iOS5,调用- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(5_0);

(1)前面3种方法这里就不多说了,很常见的系统方法.至于第四种,我在前面文章-剖析网易标签栏的效果中已经做了阐述,但是它提供的容器转场动画只可以实现一些简单的UIView动画,但是难以重用,耦合高.
(2)关键的API:
A.动画控制器 (Animation Controllers) 遵从 UIViewControllerAnimatedTransitioning 协议,并且负责实际执行动画。
B.交互控制器 (Interaction Controllers) 通过遵从 UIViewControllerInteractiveTransitioning 协议来控制可交互式的转场。
C.转场代理 (Transitioning Delegates) 根据不同的转场类型方便的提供需要的动画控制器和交互控制器。
   其中UINavigationControllerDelegate delegate 中新增了2个方法给NavigationController
               UIViewControllerTransitioningDelegate 新增transitioningDelegate  给Modal的Present和Dismis

%s__%d__|%@",__FUNCTION__,__LINE__,@"右边"); 
         self.selectedViewController = selectedViewController; 
    } else if (swipeGestureRecognizer.direction == UISwipeGestureRecognizerDirectionRight){ 
         NSLog(@"%s__%d__|%@",__FUNCTION__,__LINE__,@"左边"); 
        if (_currentControllerIndex > 0) { 
            _currentControllerIndex--; 
        } 
        UIViewController *selectedViewController = self.viewControllers[_currentControllerIndex]; 
        self.selectedViewController = selectedViewController; 
    } 

 
// 重写selectedViewController的setter 
- (void)setSelectedViewController:(UIViewController *)selectedViewController 

    NSParameterAssert (selectedViewController); 
    [self _transitionToChildViewController:selectedViewController]; 
    _selectedViewController = selectedViewController; 

 
// 切换操作(自定义的,联想我在前面文章网易标签栏切换中,系统给的transitionFromViewController,是一个道理) 
- (void)_transitionToChildViewController:(UIViewController *)toViewController 

    UIViewController *fromViewController = self.childViewControllers.count > 0 ? self.childViewControllers[0] : nil; 
    if (toViewController == fromViewController) { 
        return; 
    } 
    UIView *toView = toViewController.view; 
    [toView setTranslatesAutoresizingMaskIntoConstraints:YES]; 
    toView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 
    toView.frame = self.view.bounds; 
     
    // 自定义容器的切换,addChildViewController是关键,它保证了你想要显示的VC能够加载到容器中 
    // 而所谓的动画和上下文,只是为了转场的动画效果 
    // 因此,就算用UIScrollView切换,也不能缺少addChildViewController,切记!切记! 
    [fromViewController willMoveToParentViewController:nil]; 
    [self addChildViewController:toViewController]; 
     
    if (!fromViewController) { 
        [self.view addSubview:toViewController.view]; 
        [toViewController didMoveToParentViewController:self]; 
        return; 
    } 
     
    // Animator 
    FTMthTransitionAnimator *transitionAnimator = [[FTMthTransitionAnimator alloc] init]; 
    NSUInteger fromIndex = [self.viewControllers indexOfObject:fromViewController]; 
    NSUInteger toIndex = [self.viewControllers indexOfObject:toViewController]; 
     
    // Context 
    FTMthTransitionContext *transitionContext = [[FTMthTransitionContext alloc] initWithFromViewController:fromViewController toViewController:toViewController goingRight:(toIndex > fromIndex)]; 
    transitionContext.animated = YES; 
    transitionContext.interactive = NO; 
    transitionContext.completionBlock = ^(BOOL didComplete) { 
        // 因为是非交互式,所以fromVC可以直接直接remove出its parent's children controllers array 
        [fromViewController.view removeFromSuperview]; 
        [fromViewController removeFromParentViewController]; 
        [toViewController didMoveToParentViewController:self]; 
        if ([transitionAnimator respondsToSelector:@selector (animationEnded:)]) { 
            [transitionAnimator animationEnded:didComplete]; 
        } 
    }; 
    // 转场动画需要以转场上下文为依托,因为我们是自定义的Context,所以要手动设置 
    [transitionAnimator animateTransition:transitionContext]; 
}

大功告成.
上面展示的就是一个基本的自定义容器的非交互式的转场切换.那交互式的呢?从上面我定义手势定义为swip而不是pan也可以看出,非交互转场,并不能完全实现UIScrollView那种分页式的效果,按照类似百分比的形式来进行fromVC和toVC的切换,因为我们缺少交互控制器.在自定义的容器中,系统是没有提供返回交互控制器的协议给我们的,查了蛮多资料,也没找到给出明确的方法,我认为,要跟实现转场上下文一样,仿照系统方法,自定义的去实现交互式的协议方法.我们就要去思考,系统是如何搭建起这个环境的.


« 
» 
快速导航

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3