在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
- -(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]];
-
- }];
- }
-(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
- -(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;
- }
- }
-(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
- -(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]];
-
- }];
- }
-(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
- -(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;
- }
-(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 接口