iOS7之定製View Controller切換效果

在iOS5和iOS6前,View Controller的切換主要有4種:git

  • 1. Push/Pop,NavigationViewController常乾的事兒
  • 2. Tab,TabViewController點擊
  • 3. Present Modal,調用ViewController的presentViewController:animated:completion:方法
  • 4. Add ChildViewController,調用- (void)addChildViewController:(UIViewController *)childController方法

在使用Add ChildViewController的方式時,通常使用transitionFromViewController:toViewController:…的Animation block中能夠實現一些簡單的切換效果,這樣作有2大不足:github

  • 1. 代碼高度耦合,VC切換部分的代碼直接寫在container中,難以分離重用;
  • 2. 支持的切換效果比較有限,由於其只能使用UIView動畫來切換,管理起來也略顯麻煩

因而,蘋果在iOS7中引入了一些新的API來幫助開發者更容易,更鬆耦合地定義ViewController的轉換效果。
知識點
用法 從上面的知識點圖中能夠看出,新的API主要提供了2種VC切換的方式,一種是動畫式切換,即定義一種從一個VC到另外一個VC的動畫效果,切換的時候自動播放,第二種是交互式切換,這種方式一樣須要定義動畫效果,只是這個動畫效果會根據跟隨交互式手勢來切換VC並同時播放動畫效果。 這兩種方式的用法略有不一樣。
動畫式切換
app

  • 首先定義一個動畫類實現接口UIViewControllerAnimatedTransitioning, 實現接口的2個方法,一個是動畫效果的時間,一個是動畫效果(- (void)animateTransition:(id )transitionContext ),實現動畫效果時能夠從參數transitionContext中獲取到切換時的上下文信息,比方說從哪一個VC切換到哪一個VC等。
  • 在需切換的VC中實現UIViewControllerTransitioningDelegate,並實現animationController*方法,返回一個步驟1定義的動畫變量。
  • 調用展示VC切換方法,presentViewController等。

交互式切換動畫

  • 定義一個類實現接口UIViewControllerInteractiveTransitioning,iOS7提供了一個默認的基於百分比的動畫實現UIPercentDrivenInteractiveTransition,你們不想太麻煩能夠直接擴展這個類。該類須要綁定須要實現手勢控制的VC,同時把手勢操做添加到該VC上,而後在處理手勢動做的時候,調用接口中的方法去更新當前的動畫進度。
  • 定以切換時的動畫效果類。和動畫式切換的方式一致
  • 在需切換的VC中實現UIViewControllerTransitioningDelegate,實現interactiveController方法,返回步驟1定義的類,實現animationController方法返回步驟2定義的動畫效果。

實戰 下圖是我實現的一個VC切換的效果圖,(注:App圖片是網上隨便找的,不表明我的喜愛:)
實現代碼: VC跳入動畫的代碼:
spa

Object-c代碼 複製代碼  收藏代碼
  1. -(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext  
  2. {  
  3.     return 0.5f;  
  4. }  
  5.   
  6. -(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext  
  7. {  
  8.   
  9.     //Create the differents 3D animations  
  10.     CATransform3D viewFromTransform;  
  11.     CATransform3D viewToTransform;  
  12.       
  13.     UIView *generalContentView = [transitionContext containerView];  
  14.     UIView *fromView = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view;  
  15.     UIView *toView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view;  
  16.   
  17.       
  18.     viewFromTransform = CATransform3DMakeRotation(ROTATION_ANGLE, 0.0, 1.0, 0.0);  
  19.     viewToTransform = CATransform3DMakeRotation(-ROTATION_ANGLE, 0.0, 1.0, 0.0);  
  20.     [toView.layer setAnchorPoint:CGPointMake(0, 0.5)];  
  21.     [fromView.layer setAnchorPoint:CGPointMake(1, 0.5)];  
  22.       
  23. //    [generalContentView setTransform:CGAffineTransformMakeTranslation(generalContentView.frame.size.width/2.0, 0)];  
  24.       
  25.     viewFromTransform.m34 = PERSPECTIVE;  
  26.     viewToTransform.m34 = PERSPECTIVE;  
  27.       
  28.     toView.layer.transform = viewToTransform;  
  29.       
  30.   
  31.     //Add the to- view  
  32.     [generalContentView addSubview:toView];  
  33.       
  34.     [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{  
  35.         [generalContentView setTransform:CGAffineTransformMakeTranslation(-generalContentView.frame.size.width/2.0, 0)];  
  36.           
  37.         fromView.layer.transform = viewFromTransform;  
  38.         toView.layer.transform = CATransform3DIdentity;  
  39.           
  40.     } completion:^(BOOL finished) {  
  41.           
  42.         //Set the final position of every elements transformed  
  43.         [generalContentView setTransform:CGAffineTransformIdentity];  
  44.         fromView.layer.transform = CATransform3DIdentity;  
  45.         toView.layer.transform = CATransform3DIdentity;  
  46.         [fromView.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)];  
  47.         [toView.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)];  
  48.           
  49.           
  50.         if ([transitionContext transitionWasCancelled]) {  
  51.             [toView removeFromSuperview];  
  52.         } else {  
  53.             [fromView removeFromSuperview];  
  54.         }  
  55.           
  56.         // inform the context of completion  
  57.         [transitionContext completeTransition:![transitionContext transitionWasCancelled]];  
  58.           
  59.     }];  
  60. }  
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return 0.5f;
}

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

    //Create the differents 3D animations
    CATransform3D viewFromTransform;
    CATransform3D viewToTransform;
    
    UIView *generalContentView = [transitionContext containerView];
    UIView *fromView = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view;
    UIView *toView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view;

    
    viewFromTransform = CATransform3DMakeRotation(ROTATION_ANGLE, 0.0, 1.0, 0.0);
    viewToTransform = CATransform3DMakeRotation(-ROTATION_ANGLE, 0.0, 1.0, 0.0);
    [toView.layer setAnchorPoint:CGPointMake(0, 0.5)];
    [fromView.layer setAnchorPoint:CGPointMake(1, 0.5)];
    
//    [generalContentView setTransform:CGAffineTransformMakeTranslation(generalContentView.frame.size.width/2.0, 0)];
    
    viewFromTransform.m34 = PERSPECTIVE;
    viewToTransform.m34 = PERSPECTIVE;
    
    toView.layer.transform = viewToTransform;
    

    //Add the to- view
    [generalContentView addSubview:toView];
    
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        [generalContentView setTransform:CGAffineTransformMakeTranslation(-generalContentView.frame.size.width/2.0, 0)];
        
        fromView.layer.transform = viewFromTransform;
        toView.layer.transform = CATransform3DIdentity;
        
    } completion:^(BOOL finished) {
        
        //Set the final position of every elements transformed
        [generalContentView setTransform:CGAffineTransformIdentity];
        fromView.layer.transform = CATransform3DIdentity;
        toView.layer.transform = CATransform3DIdentity;
        [fromView.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)];
        [toView.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)];
        
        
        if ([transitionContext transitionWasCancelled]) {
            [toView removeFromSuperview];
        } else {
            [fromView removeFromSuperview];
        }
        
        // inform the context of completion
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        
    }];
}

手勢交互代碼:code

Object-c代碼 複製代碼  收藏代碼
  1. -(void)wireToViewController:(UIViewController *)viewController  
  2. {  
  3.     self.presentingVC = viewController;  
  4.     [self prepareGestureRecognizerInView:viewController.view];  
  5. }  
  6.   
  7. - (void)prepareGestureRecognizerInView:(UIView*)view {  
  8.     UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];  
  9.     [view addGestureRecognizer:gesture];  
  10. }  
  11.   
  12. -(CGFloat)completionSpeed  
  13. {  
  14.     return 1 - self.percentComplete;  
  15. }  
  16.   
  17. - (void)handleGesture:(UIPanGestureRecognizer *)gestureRecognizer {  
  18.     CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view.superview];  
  19.     switch (gestureRecognizer.state) {  
  20.         case UIGestureRecognizerStateBegan:  
  21.             // 1. Mark the interacting flag. Used when supplying it in delegate.  
  22.             self.interacting = YES;  
  23.             [self.presentingVC dismissViewControllerAnimated:YES completion:nil];  
  24.             break;  
  25.         case UIGestureRecognizerStateChanged: {  
  26.             // 2. Calculate the percentage of guesture  
  27.             CGFloat fraction = -translation.x / 300.0;  
  28.             //Limit it between 0 and 1  
  29.             fraction = fminf(fmaxf(fraction, 0.0), 1.0);  
  30.             self.shouldComplete = (fraction > 0.5);  
  31.               
  32.             [self updateInteractiveTransition:fraction];  
  33.             break;  
  34.         }  
  35.         case UIGestureRecognizerStateEnded:  
  36.         case UIGestureRecognizerStateCancelled: {  
  37.             // 3. Gesture over. Check if the transition should happen or not  
  38.             self.interacting = NO;  
  39.             if (!self.shouldComplete || gestureRecognizer.state == UIGestureRecognizerStateCancelled) {  
  40.                 [self cancelInteractiveTransition];  
  41.             } else {  
  42.                 [self finishInteractiveTransition];  
  43.             }  
  44.             break;  
  45.         }  
  46.         default:  
  47.             break;  
  48.     }  
  49. }  
-(void)wireToViewController:(UIViewController *)viewController
{
    self.presentingVC = viewController;
    [self prepareGestureRecognizerInView:viewController.view];
}

- (void)prepareGestureRecognizerInView:(UIView*)view {
    UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
    [view addGestureRecognizer:gesture];
}

-(CGFloat)completionSpeed
{
    return 1 - self.percentComplete;
}

- (void)handleGesture:(UIPanGestureRecognizer *)gestureRecognizer {
    CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view.superview];
    switch (gestureRecognizer.state) {
        case UIGestureRecognizerStateBegan:
            // 1. Mark the interacting flag. Used when supplying it in delegate.
            self.interacting = YES;
            [self.presentingVC dismissViewControllerAnimated:YES completion:nil];
            break;
        case UIGestureRecognizerStateChanged: {
            // 2. Calculate the percentage of guesture
            CGFloat fraction = -translation.x / 300.0;
            //Limit it between 0 and 1
            fraction = fminf(fmaxf(fraction, 0.0), 1.0);
            self.shouldComplete = (fraction > 0.5);
            
            [self updateInteractiveTransition:fraction];
            break;
        }
        case UIGestureRecognizerStateEnded:
        case UIGestureRecognizerStateCancelled: {
            // 3. Gesture over. Check if the transition should happen or not
            self.interacting = NO;
            if (!self.shouldComplete || gestureRecognizer.state == UIGestureRecognizerStateCancelled) {
                [self cancelInteractiveTransition];
            } else {
                [self finishInteractiveTransition];
            }
            break;
        }
        default:
            break;
    }
}

根據手勢3D切換的動畫代碼:orm

Object-c代碼 複製代碼  收藏代碼
  1. -(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext  
  2. {  
  3.     return 0.5f;  
  4. }  
  5.   
  6. -(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext  
  7. {  
  8.   
  9.     //Create the differents 3D animations  
  10.     CATransform3D viewFromTransform;  
  11.     CATransform3D viewToTransform;  
  12.       
  13.     UIView *generalContentView = [transitionContext containerView];  
  14.     UIView *fromView = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view;  
  15.     UIView *toView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view;  
  16.   
  17.       
  18.     viewFromTransform = CATransform3DMakeRotation(ROTATION_ANGLE, 0.0, 1.0, 0.0);  
  19.     viewToTransform = CATransform3DMakeRotation(-ROTATION_ANGLE, 0.0, 1.0, 0.0);  
  20.     [toView.layer setAnchorPoint:CGPointMake(0, 0.5)];  
  21.     [fromView.layer setAnchorPoint:CGPointMake(1, 0.5)];  
  22.       
  23. //    [generalContentView setTransform:CGAffineTransformMakeTranslation(generalContentView.frame.size.width/2.0, 0)];  
  24.       
  25.     viewFromTransform.m34 = PERSPECTIVE;  
  26.     viewToTransform.m34 = PERSPECTIVE;  
  27.       
  28.     toView.layer.transform = viewToTransform;  
  29.       
  30.   
  31.     //Add the to- view  
  32.     [generalContentView addSubview:toView];  
  33.       
  34.     [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{  
  35.         [generalContentView setTransform:CGAffineTransformMakeTranslation(-generalContentView.frame.size.width/2.0, 0)];  
  36.           
  37.         fromView.layer.transform = viewFromTransform;  
  38.         toView.layer.transform = CATransform3DIdentity;  
  39.           
  40.     } completion:^(BOOL finished) {  
  41.           
  42.         //Set the final position of every elements transformed  
  43.         [generalContentView setTransform:CGAffineTransformIdentity];  
  44.         fromView.layer.transform = CATransform3DIdentity;  
  45.         toView.layer.transform = CATransform3DIdentity;  
  46.         [fromView.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)];  
  47.         [toView.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)];  
  48.           
  49.           
  50.         if ([transitionContext transitionWasCancelled]) {  
  51.             [toView removeFromSuperview];  
  52.         } else {  
  53.             [fromView removeFromSuperview];  
  54.         }  
  55.           
  56.         // inform the context of completion  
  57.         [transitionContext completeTransition:![transitionContext transitionWasCancelled]];  
  58.           
  59.     }];  
  60. }  
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return 0.5f;
}

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

    //Create the differents 3D animations
    CATransform3D viewFromTransform;
    CATransform3D viewToTransform;
    
    UIView *generalContentView = [transitionContext containerView];
    UIView *fromView = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view;
    UIView *toView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view;

    
    viewFromTransform = CATransform3DMakeRotation(ROTATION_ANGLE, 0.0, 1.0, 0.0);
    viewToTransform = CATransform3DMakeRotation(-ROTATION_ANGLE, 0.0, 1.0, 0.0);
    [toView.layer setAnchorPoint:CGPointMake(0, 0.5)];
    [fromView.layer setAnchorPoint:CGPointMake(1, 0.5)];
    
//    [generalContentView setTransform:CGAffineTransformMakeTranslation(generalContentView.frame.size.width/2.0, 0)];
    
    viewFromTransform.m34 = PERSPECTIVE;
    viewToTransform.m34 = PERSPECTIVE;
    
    toView.layer.transform = viewToTransform;
    

    //Add the to- view
    [generalContentView addSubview:toView];
    
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        [generalContentView setTransform:CGAffineTransformMakeTranslation(-generalContentView.frame.size.width/2.0, 0)];
        
        fromView.layer.transform = viewFromTransform;
        toView.layer.transform = CATransform3DIdentity;
        
    } completion:^(BOOL finished) {
        
        //Set the final position of every elements transformed
        [generalContentView setTransform:CGAffineTransformIdentity];
        fromView.layer.transform = CATransform3DIdentity;
        toView.layer.transform = CATransform3DIdentity;
        [fromView.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)];
        [toView.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)];
        
        
        if ([transitionContext transitionWasCancelled]) {
            [toView removeFromSuperview];
        } else {
            [fromView removeFromSuperview];
        }
        
        // inform the context of completion
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        
    }];
}

待切換的VC的動畫效果配置代碼:blog

Object-c代碼 複製代碼  收藏代碼
  1. -(IBAction)changeViewController:(id)sender  
  2. {  
  3.     ToViewController *vc =[[ToViewController alloc] init];  
  4.     vc.delegate = self;  
  5.     vc.transitioningDelegate = self;  
  6.     [self.interactionAnimation wireToViewController:vc];  
  7.     [self presentViewController:vc animated:YES completion:nil];  
  8. }  
  9.   
  10. -(void) didClickedDismissButton:(ToViewController *)viewController  
  11. {  
  12.     [self dismissViewControllerAnimated:YES completion:nil];  
  13. }  
  14.   
  15. - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source  
  16. {  
  17.     return self.presentAnimation;  
  18. }  
  19.   
  20. -(id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed  
  21. {  
  22.     return self.cubeAnimation;  
  23. }  
  24.   
  25. -(id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator  
  26. {  
  27.     return self.interactionAnimation.interacting? self.interactionAnimation:nil;  
  28. }  
-(IBAction)changeViewController:(id)sender
{
    ToViewController *vc =[[ToViewController alloc] init];
    vc.delegate = self;
    vc.transitioningDelegate = self;
    [self.interactionAnimation wireToViewController:vc];
    [self presentViewController:vc animated:YES completion:nil];
}

-(void) didClickedDismissButton:(ToViewController *)viewController
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
    return self.presentAnimation;
}

-(id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    return self.cubeAnimation;
}

-(id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator
{
    return self.interactionAnimation.interacting? self.interactionAnimation:nil;
}

整個示例的完整代碼已提交到Github上:https://github.com/xianlinbox/iOS7_New/tree/master/iOS7_New/VCTransitions 接口

相關文章
相關標籤/搜索