原文地址:iOS7之定製View Controller切換效果
在iOS5和iOS6前,View Controller的切換主要有4種:
- 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大不足:
- 1. 代碼高度耦合,VC切換部分的代碼直接寫在container中,難以分離重用;
- 2. 支持的切換效果比較有限,因爲其只能使用UIView動畫來切換,管理起來也略顯麻煩
於是,蘋果在iOS7中引入了一些新的API來幫助開發者更容易,更鬆耦合地定義ViewController的轉換效果。
知識點
用法
從上面的知識點圖中可以看出,新的API主要提供了2種VC切換的方式,一種是動畫式切換,即定義一種從一個VC到另一個VC的動畫效果,切換的時候自動播放,第二種是交互式切換,這種方式同樣需要定義動畫效果,只是這個動畫效果會根據跟隨交互式手勢來切換VC並同時播放動畫效果。 這兩種方式的用法略有不同。
動畫式切換
- 首先定義一個動畫類實現接口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跳入動畫的代碼:
- -(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]];
- }];
- }
手勢交互代碼:
- -(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切換的動畫代碼:
- -(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的動畫效果配置代碼:
- -(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