您的位置:首页 > 产品设计 > UI/UE

UIVIewController自定义切换效果

2015-12-26 20:43 344 查看
iOS中常见的viewController切换有四种:模态视图,导航栏控制器,UITabBarController以及addChildViewController,自定义viewController动画切换也是iOS7中的新特性,常见的操作如下:


一、基本介绍

熟悉SDK中有关这部分内容的相关接口以及它们的关系和典型用法。相关的内容都定义在UIKit的UIViewControllerTransitioning.h中:


1、@protocol UIViewControllerContextTransitioning
<NSObject>

这个接口用来提供切换上下文给开发者使用,包含了从哪个VC到哪个VC等各类信息,一般不需要开发者自己实现。具体来说,iOS7的自定义切换目的之一就是切换相关代码解耦,在进行VC切换时,做切换效果实现的时候要需要切换前后VC的一些信息,系统在新加入的API的比较的地方都会提供一个实现了该接口的对象,以供我们使用。


对于切换的动画实现来说,这个接口最重要的方法有:

(1)、- (UIView *)containerView;//VC切换所发生的view容器,开发者应该将切除的view移除,将切入的view加入到view容器中。

(2)、- (UIViewController )viewControllerForKey:(NSString )key;//提供一个key,返回对应的VC。现在的SDK中key的选择只有UITransitionContextFromViewControllerKey和 UITransitionContextToViewControllerKey两种,分别表示将要切出和切入的VC。

(3)、- (CGRect)initialFrameForViewController:(UIViewController *)vc;//某个VC的初始位置,可以用来做动画的计算。

(4)、- (CGRect)finalFrameForViewController:(UIViewController *)vc;//与上面的方法对应,得到切换结束时某个VC应在的frame。

(5)、- (void)completeTransition:(BOOL)didComplete;//向这个context报告切换已经完成。

2、@protocol UIViewControllerAnimatedTransitioning
<NSObject>

这个接口负责切换的具体内容,也即“切换中英爱发生什么”。开发者在左自定义切换效果时大部分代码回事用来实现这个接口。它只有两个方法需要我们实现:


(1)、- (NSTimeInterval)transitionDuration:(id
<UIViewControllerContextTransitioning>
)transitionContext;//系统给出一个切换上下文,我们根据上下文环境返回这个切换所需要的花费时间(一般就返回动画的时间就好了,SDK会勇这个时间来在百分比驱动的切换中进行帧的计算,后面再详细展开)。

(2)、- (void)animateTransition:(id
<UIViewControllerContextTransitioning>
)transitionContext;//在进行切换的时候调用该方法,我们对于切换时UIView的设置和动画都在这个方法中完成。

3、 @protocol UIViewControllerTransitioningDelegate
<NSObject>

这个接口的作用比较简单单一,在需要VC切换的时候系统会像实现了这个接口的对象询问是否需要使用自定义的切换效果。这个接口共有四个类似的方法:


(1)、- (id )animationControllerForPresentedController:(UIViewController )presented presentingController:(UIViewController )presenting sourceController:(UIViewController *)source;

(2)、- (id )animationControllerForDismissedController:(UIViewController *)dismissed;

(3)、- (id )interactionControllerForPresentation:(id )animator;

(4)、- (id )interactionControllerForDismissal:(id )animator;

前两个方法针对动画切换的,我们需要分别在呈现VC和解散VC时,给出一个实现实现了UIViewControllerAnimatedTransitioning接口的对象(其中包含切换时长和如何切换)。后两个方法涉及交互式切换。

二、模态视图自定义切换

1、创建一个模态视图控制器

//CXToModalVC.h

#import <UIKit/UIKit.h>
@class CXToModalVC;
@protocol CXToModalVCDelegate <NSObject>
-(void)dismissViewController:(CXToModalVC *)mcv;
@end
@interface CXToModalVC : UIViewController
@property(nonatomic, weak)id<CXToModalVCDelegate> delegate;
@end


//CXToModalVC.m

#import "CXToModalVC.h"
@interface CXToModalVC ()
@end
@implementation CXToModalVC
- (void)viewDidLoad {
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor greenColor]];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(dismissViewController) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"dismiss" forState:UIControlStateNormal];
[button setFrame:CGRectMake(0, 0, 130, 200)];
[self.view addSubview:button];
}
-(void)dismissViewController {
[self.delegate dismissViewController:self];
}
@end


2、建立主视图控制器,实现CXToModalVCDelegate协议

//CXFromModalVC.h

#import <UIKit/UIKit.h>
#import "CXToModalVC.h"
@interface CXFromModalVC : UIViewController<CXToModalVCDelegate>
@end
//CXFromModalVC.m
#import "CXFromModalVC.h"
@interface CXFromModalVC ()
@end
@implementation CXFromModalVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(pushViewController) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"present" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setFrame:CGRectMake(0, 0, 130, 200)];
[self.view addSubview:button];
}
-(void)pushViewController {
CXToModalVC *toModalVC = [[CXToModalVC alloc]init];
toModalVC.delegate = self;
toModalVC.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:toModalVC animated:YES completion:nil];
}
#pragma mark -- CXModalViewControllerDelegate
-(void)dismissViewController:(CXToModalVC *)mcv {
[self dismissViewControllerAnimated:YES completion:nil];
}
@end


上面代码实现模态视图的切换,通过toModalVC.modalTransitionStyle = UIModalTransitionStyleCoverVertical还可以设置切换的动画效果,iOS内置了几种切换效果供开发者使用,但是我们现在需要自定义动画效果。


3、新建一个类实现UIViewControllerAnimatedTransitioning协议

新建一个类实现UIViewControllerAnimatedTransitioning协议,这个类就是我们的动画切换类,iOS是吸纳了动画切换与试图控制类的解耦,编写一个动画切换类可以反复重用。


//CXModalTransitionAnimation.h

#import <UIKit/UIKit.h>
typedef  enum{
AnimationTypeDismiss,
AnimationTypePresent
}AnimationType;
@interface CXModalTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning>
@property (nonatomic ,assign)AnimationType animationType;
@end


//CXModalTransitionAnimation.m

#import "CXModalTransitionAnimation.h"
@implementation CXModalTransitionAnimation
/* 动画持续时间,单位是秒 */
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 1;
}
/* 动画效果 */
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
//通过键值UITransitionContextToViewControllerKey获取需要呈现的视图控制器toVC
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//得到toVC完全呈现后的frame
CGRect finalFrame = [transitionContext finalFrameForViewController:toVC];
if (self.animationType == AnimationTypePresent) {
//需要呈现的视图是模态视图,此时将模态视图的frame放到屏幕空间下方,这样才能实现从下方弹出的效果
toVC.view.frame = CGRectOffset(finalFrame, 0, [UIScreen mainScreen].bounds.size.height);
}else{
//需要呈现的视图是主视图,此时将主视图的frame放在屏幕空间上方,这样才能实现从上方放下的效果
toVC.view.frame = CGRectOffset(finalFrame, 0, -[UIScreen mainScreen].bounds.size.height);
}
//切换在containerView中完成,需要将toVC.view加到containerView中
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toVC.view];
//开始动画,这里使用了UIKit提供的弹簧效果动画,usingSpringWithDamping越接近1弹性效果越不明显,此API在IOS7之后才能使用
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
toVC.view.frame = finalFrame;
} completion:^(BOOL finished) {
//通知系统动画切换完成
[transitionContext completeTransition:YES];
}];
}
@end


上面的代码实现了从屏幕下方弹性弹出CXModalViewController以及CXModalViewController弹回屏幕下方的动画效果


4、重新配置主视图控制器,使用我们自定义的动画

//CXFromModalVC.h

#import <UIKit/UIKit.h>
#import "CXToModalVC.h"
//如果需要使用自定义动画,视图需要实现UIViewControllerTransitioningDelegate协议
@interface CXFromModalVC : UIViewController<CXToModalVCDelegate,UIViewControllerTransitioningDelegate>
@end


//CXFromModalVC.m

#import "CXFromModalVC.h"
#import "CXModalTransitionAnimation.h"
@interface CXFromModalVC ()
@property (nonatomic ,strong)CXModalTransitionAnimation* animation;
@end
@implementation CXFromModalVC
- (void)viewDidLoad {
[super viewDidLoad];
_animation = [[CXModalTransitionAnimation alloc]init];
self.view.backgroundColor = [UIColor redColor];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(pushViewController) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"present" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setFrame:CGRectMake(0, 0, 130, 200)];
[self.view addSubview:button];
}
-(void)pushViewController {
CXToModalVC *toModalVC = [[CXToModalVC alloc]init];
toModalVC.transitioningDelegate = self;
_animation.animationType = AnimationTypePresent;
toModalVC.delegate = self;
toModalVC.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:toModalVC animated:YES completion:nil];
}
#pragma mark -- CXModalViewControllerDelegate
-(void)dismissViewController:(CXToModalVC *)mcv {
_animation.animationType = AnimationTypeDismiss;
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark -- UIViewControllerTransitioningDelegate
// 用于动画
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
return _animation;
}
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
return _animation;
}
@end


我们设置了模态是图控制器的transitioningDelegate为self,当present和dismiss模态是图时系统会像实现了这个接口的对象询问是否需要使用自定义的切换效果。


这个接口共有四个类似的方法:

-(id< UIViewControllerAnimatedTransitioning >)animationControllerForPresentedController:(UIViewController )presented presentingController:(UIViewController )presenting sourceController:(UIViewController *)source;

-(id< UIViewControllerAnimatedTransitioning >)animationControllerForDismissedController:(UIViewController *)dismissed;

-(id< UIViewControllerInteractiveTransitioning >)interactionControllerForPresentation:(id < UIViewControllerAnimatedTransitioning >)animator;

-(id< UIViewControllerInteractiveTransitioning >)interactionControllerForDismissal:(id < UIViewControllerAnimatedTransitioning >)animator;

我们实现了前两个方法用来设置模态视图出现和消失的动画,后两个方法用来处理交互式动画

5、运行代码,可以看到模态试图自定义的动画效果

三、 UINavigationController自定义动画切换

1、创建我们的动画切换类

//CXNavigationTransitionAnimation.h

#import <UIKit/UIKit.h>
@interface CXNavigationTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning>
@end


//CXNavigationTransitionAnimation.m

#import "CXNavigationTransitionAnimation.h"
@implementation CXNavigationTransitionAnimation
/* 动画持续时间0.7秒 */
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 0.7;
}
/* 页面push和pop动画 */
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
//通过键值UITransitionContextToViewControllerKey获得需要呈现的试图控制器
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//通过键值UITransitionContextFromViewControllerKey获得需要退出的试图控制器
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[[transitionContext containerView] addSubview:toVC.view];
//设置需要呈现的试图控制器透明
[toVC.view setAlpha:0];
//设置需要呈现的试图控制器位于左侧屏幕外,且大小为0.1倍,这样才有从左侧推入屏幕,且逐渐变大的动画效果
toVC.view.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(-[UIScreen mainScreen].bounds.size.width, 0), 0.1, 0.1);
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
//将需要退出的试图控制器移出右侧屏幕外,且大小为原来的0.1倍
fromVC.view.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation([UIScreen mainScreen].bounds.size.width, 0), 0.1, 0.1);
fromVC.view.alpha = 0;
//将动画还原动画进行之前的状态
toVC.view.transform = CGAffineTransformIdentity;
toVC.view.alpha = 1;
} completion:^(BOOL finished) {
//动画结束后属性设为初始值
fromVC.view.transform = CGAffineTransformIdentity;
fromVC.view.alpha = 1;
//通知系统动画切换成功
[transitionContext completeTransition:YES];
}];
}
@end


2、创建FromViewController试图控制器

//FromViewController.h

#import <UIKit/UIKit.h>
#import "CXNavigationTransitionAnimation.h"
@interface FromViewController : UIViewController<UINavigationControllerDelegate>
@property (nonatomic ,strong)CXNavigationTransitionAnimation* animation;
@end


//FromViewController.m

#import "FromViewController.h"
#import "ToViewController.h"
@interface FromViewController ()
@end
@implementation FromViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor yellowColor];
// Push
UIButton *pushButton = [UIButton buttonWithType:UIButtonTypeSystem];
pushButton.frame = CGRectMake(140, 200, 40, 40);
[pushButton setTitle:@"Push" forState:UIControlStateNormal];
[pushButton addTarget:self action:@selector(push) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:pushButton];
self.animation = [[CXNavigationTransitionAnimation alloc] init];
}
- (void)push{
ToViewController* toView = [[ToViewController alloc] init];
self.navigationController.delegate = self;
[self.navigationController pushViewController:toView animated:YES];
}
#pragma mark -- UINavigationControllerDelegate
// 动画特效
- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
if (operation == UINavigationControllerOperationPop) {
return _animation;
}
return nil;
}
@end


UINavigationControllerDelegate中有两个方法与视图控制器切换动画相关

(1)、- (id < UIViewControllerInteractiveTransitioning >)navigationController:(UINavigationController*)navigationController interactionControllerForAnimationController:(id < UIViewControllerAnimatedTransitioning >) animationController

(2)、- (id < UIViewControllerAnimatedTransitioning >)navigationController:(UINavigationController*)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController )fromVC toViewController:(UIViewController )toVC

其中第一个方法用于交互式动画

3、创建ToViewController试图控制器

//ToViewController.h

#import <UIKit/UIKit.h>
@interface ToViewController : UIViewController
@end


//ToViewController.m

#import "ToViewController.h"
@interface ToViewController ()
@end
@implementation ToViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
//pop
UIButton *popButton = [UIButton buttonWithType:UIButtonTypeSystem];
popButton.frame = CGRectMake(140, 200, 40, 40);
[popButton setTitle:@"Pop" forState:UIControlStateNormal];
[popButton addTarget:self action:@selector(pop) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:popButton];
}
- (void)pop{
[self.navigationController popViewControllerAnimated:YES];
}
@end


4、运行查看动画效果

四、UITabBarController自定义动画切换

1、创建一个UITabBarController视图控制器,给其添加两个viewController

代码略。

2、创建我们的动画切换类

//CXTabbarTransitionAnimation.h

#import <UIKit/UIKit.h>
@interface CXTabbarTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning>
@end


//CXTabbarTransitionAnimation.m

#import "CXTabbarTransitionAnimation.h"
#define PERSPECTIVE -1.0/200
@implementation CXTabbarTransitionAnimation
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 0.7;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
CATransform3D viewFromTransform = CATransform3DMakeRotation(M_PI/2, 0, 1, 0);
CATransform3D viewToTransform = CATransform3DMakeRotation(-M_PI/2, 0, 1, 0);
viewFromTransform.m34 = PERSPECTIVE;
viewToTransform.m34 = PERSPECTIVE;
//获取from和to视图
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIView *container = [transitionContext containerView];
[toVC.view.layer setAnchorPoint:CGPointMake(0, 0.5)];
[fromVC.view.layer setAnchorPoint:CGPointMake(1, 0.5)];
toVC.view.layer.transform = viewToTransform;
[container addSubview:toVC.view];
container.transform = CGAffineTransformMakeTranslation(container.frame.size.width/2.0,0);
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromVC.view.layer.transform = viewFromTransform;
toVC.view.layer.transform = CATransform3DIdentity;
[container setTransform:CGAffineTransformMakeTranslation(-container.frame.size.width/2.0, 0)];
} completion:^(BOOL finished) {
fromVC.view.layer.transform = CATransform3DIdentity;
toVC.view.layer.transform = CATransform3DIdentity;
[fromVC.view.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)];
[toVC.view.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)];
[container setTransform:CGAffineTransformIdentity];
[transitionContext completeTransition:YES];
}];
}
@end


3、为UITabbarController添加动画效果,相关协议为UITabbarControllerDelegate

//CXFromTabVC.m

#import "CXFromTabVC.h"
#import "CXTabbarTransitionAnimation.h"
@interface CXFromTabVC ()<UITabBarControllerDelegate>
@property(nonatomic ,strong)CXTabbarTransitionAnimation* animation;
@end

@implementation CXFromTabVC

- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor yellowColor];
self.tabBarController.delegate = self;
self.animation = [[CXTabbarTransitionAnimation alloc]init];
}
#pragma mark -- UITabBarControllerDelegate
- (id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC  NS_AVAILABLE_IOS(7_0) {
return _animation;
}
@end


4、运行查看效果

五、模态视图交互式切换

前面所有的切换都是当点击完一个按钮后就立刻执行,但是有时候我们希望某些切换操作可以进行到一半取消,比如我们为前一个模态视图切换提供手势支持,随着手势向下拉,模态视图慢慢退出屏幕底部,但当我们拉倒一半取消或者向上拉,模态视图就会回到之前的状态,这就是所谓的交互式切换。


1、创建动画效果类:

//CXModalMoveTransitionAnimation.h

#import <UIKit/UIKit.h>
@interface CXModalMoveTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning>
@end


//CXModalMoveTransitionAnimation.m

#import "CXModalMoveTransitionAnimation.h"
@implementation CXModalMoveTransitionAnimation
/* 动画持续时间0.7秒 */
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 1;
}
/* 动画 */
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
CGRect screenBounds = [[UIScreen mainScreen] bounds];
//初始位置
CGRect initFrame = [transitionContext initialFrameForViewController:fromVC];
CGRect finalFrame = CGRectOffset(initFrame, 0, screenBounds.size.height);
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toVC.view];
[containerView sendSubviewToBack:toVC.view];
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromVC.view.frame = finalFrame;
NSLog(@"%f--%f--%f--%f",finalFrame.size.width,finalFrame.size.height,finalFrame.origin.x,finalFrame.origin.y);
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}


上面代码中值得注意的是最后[transitionContext completeTransition:![transitionContext transitionWasCancelled]];因为在交互式切换中切换可能会取消,所以这里使用[transitionContext transitionWasCancelled]判断是否成功。


2、添加手势

首先我们需要了解一些知识:

UIViewControllerContextTransitioning,系统提供的VC切换上下文,其里边有三个关于InteractionTransition的方法,正式用来处理交互切换的。但是在初级的实际使用中我们可以不太理会它们,而是使用iOS 7 SDK已经给我们准备好的一个现成转为交互切换而新加的类:

UIPercentDrivenInteractiveTransition。
UIPercentDrivenInteractiveTransition:


这是一个实现了UIViewControllerInteractiveTransitioning接口的类,为我们预先实现和提供了一系列便利的方法,可以用一个百分比来控制交互式切换的过程。一般来说我们更多的会使用某些手势来完成交互式的转移,这样使用这个类(一般是其子类)的话就会非常方便。我们在手势识别中只需要告诉这个类的实力当前的状态百分比如何,系统边根据这个百分比和我们之前设定的迁移方式为我们计算当前应该的UI渲染,十分方便。具体的几个重要方法:

(1)、- (void)updateInteractiveTransition:(CGFloat)percentComplete; //更新百分比,一般通过手势识别的长度之类的来计算一个值,然后进行更新。

(2)、- (void)cancelInteractiveTransition; //报告交互取消,返回切换前的状态。

(3)、- (void)finishInteractiveTransition; //报告交互完成,更新到切换后的状态。

@protocol UIViewControllerInteractiveTransitioning

就如上面提到的,UIPercentDrivenInteractiveTransition只是实现了这个接口的一个类。为了交互切换的功能,我们需要实现这个接口。

//CXModalInterActiveTransitionAnimation.h

#import <UIKit/UIKit.h>
@interface CXModalInterActiveTransitionAnimation : UIPercentDrivenInteractiveTransition
@property(nonatomic,assign)BOOL interacting;
- (void)wireToViewController:(UIViewController*)viewController;
@end


//CXModalInterActiveTransitionAnimation.m

#import "CXModalInterActiveTransitionAnimation.h"
@interface CXModalInterActiveTransitionAnimation()
@property (nonatomic, strong) UIViewController *presentingVC;
@property (nonatomic, assign) BOOL shouldComplete;
@end
@implementation CXModalInterActiveTransitionAnimation
/* 对外接口 为push的页面添加手势 */
- (void)wireToViewController:(UIViewController*)viewController{
_presentingVC = viewController;
//添加手势
UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
[viewController.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:{
_interacting = YES;
[self.presentingVC dismissViewControllerAnimated:YES completion:nil];
break;
}
case UIGestureRecognizerStateChanged: {
CGFloat fraction = translation.y / 400.0;
fraction = fminf(fmaxf(fraction, 0.0), 1.0);
_shouldComplete = (fraction > 0.5);
/* 此处不能设置1 否则会出现问题 */
if (fraction >= 0.99) {
fraction = 0.99;
}
[self updateInteractiveTransition:fraction];
break;
}
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled: {
_interacting = NO;
if (!_shouldComplete || gestureRecognizer.state == UIGestureRecognizerStateCancelled || [gestureRecognizer velocityInView:gestureRecognizer.view].y < 0 ) {
[self cancelInteractiveTransition];
} else {
[self finishInteractiveTransition];
}
break;
}
default:
break;
}
}
@end


上面的代码中的interacting用来判断当前是否处于交互视图切换过程中,只有在这个过程中我们才需要在

- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator;中使用动画。


在此处我们为View添加手势,然后子啊手势中根据情况调用UIPercentDrivenInteractiveTransition的三个方法,最后在interactionControllerForDismissal中返回我们定义的动画,这样就可以实现交互式动画切换了。

3、添加交互式动画切换

//CXFromMoveVC.m

#import "CXFromMoveVC.h"
#import "CXModalMoveTransitionAnimation.h"
#import "CXModalInterActiveTransitionAnimation.h"
#import "CXModalTransitionAnimation.h"
#import "CXToMoveVC.h"
@interface CXFromMoveVC ()<CXToMoveVCDelegate,UIViewControllerTransitioningDelegate>
@property(nonatomic,strong)CXModalTransitionAnimation *animation;
@property(nonatomic,strong)CXModalInterActiveTransitionAnimation *interActive;
@property(nonatomic,strong)CXModalMoveTransitionAnimation *interActiveAnimation;
@end
@implementation CXFromMoveVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor yellowColor];
/* 初始化各种数据 */
_animation = [[CXModalTransitionAnimation alloc] init];
_interActive = [[CXModalInterActiveTransitionAnimation alloc] init];
_interActiveAnimation = [[CXModalMoveTransitionAnimation alloc] init];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(presentViewController) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"present" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setFrame:CGRectMake(0, 0, 130, 200)];
[self.view addSubview:button];
}
-(void)presentViewController {
CXToMoveVC *controller = [[CXToMoveVC alloc] init];
controller.delegate = self;
controller.transitioningDelegate = self;
[_interActive wireToViewController:controller];
[self presentViewController:controller animated:YES completion:nil];
}
#pragma mark -- CXToMoveVCDelegate
-(void)dismissViewController:(CXToMoveVC *)mcv {
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark -- UIViewControllerTransitioningDelegate
/* 模态视图消失和出现动画 */
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
return _animation;
}
/* 交互式动画 */
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
return self.interActive.interacting ? _interActiveAnimation : _animation;
}
- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator {
return self.interActive.interacting ? self.interActive : nil;
}
@end


六、UINavigationController交互式动画切换

1、添加手势,在手势中调用UIPercentDrivenInteractiveTransition的三个方法

2、创建动画

//CXMoveNavigationTransitionAnimation.h

#import <UIKit/UIKit.h>
@interface CXMoveNavigationTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning>
@end


//CXMoveNavigationTransitionAnimation.m

#import "CXMoveNavigationTransitionAnimation.h"
@implementation CXMoveNavigationTransitionAnimation
//动画持续时间0.7秒
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 0.7;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
//通过键值UITransitionContextToViewControllerKey获得需要呈现的试图控制器
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//通过键值UITransitionContextFromViewControllerKey获得需要退出的试图控制器
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[[transitionContext containerView] addSubview:toVC.view];
//设置需要呈现的试图控制器透明
[toVC.view setAlpha:0];
//设置需要呈现的试图控制器位于左侧屏幕外,且大小为0.1倍,这样才有从左侧推入屏幕,且逐渐变大的动画效果
toVC.view.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(-[UIScreen mainScreen].bounds.size.width, 0), 0.1, 0.1);
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
//将需要退出的试图控制器移出右侧屏幕外,且大小为原来的0.1倍
fromVC.view.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation([UIScreen mainScreen].bounds.size.width, 0), 0.1, 0.1);
fromVC.view.alpha = 0;
toVC.view.transform = CGAffineTransformIdentity;
toVC.view.alpha = 1;
} completion:^(BOOL finished) {
//动画结束后属性设为初始值
fromVC.view.transform = CGAffineTransformIdentity;
fromVC.view.alpha = 1;
toVC.view.transform = CGAffineTransformIdentity;
toVC.view.alpha = 1;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
@end


3、在相应的代理方法中返回我们定义的动画

在这里我们直接使用UIPercentDrivenInteractiveTransition。

//CXFromMoveNVC.h

#import <UIKit/UIKit.h>
#import "CXMoveNavigationTransitionAnimation.h"
@interface CXFromMoveNVC : UIViewController<UINavigationControllerDelegate>
@property(nonatomic, strong)CXMoveNavigationTransitionAnimation*animation;
@property(nonatomic, strong)UIPercentDrivenInteractiveTransition* interactionController;
@property (nonatomic,assign)BOOL interActiving;
@end


//CXFromMoveNVC.m

#import "CXFromMoveNVC.h"
#import "CXToMoveNVC.h"
@interface CXFromMoveNVC ()
@end
@implementation CXFromMoveNVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor yellowColor];
// Push
UIButton *pushButton = [UIButton buttonWithType:UIButtonTypeSystem];
pushButton.frame = CGRectMake(140, 200, 40, 40);
[pushButton setTitle:@"Push" forState:UIControlStateNormal];
[pushButton addTarget:self action:@selector(push) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:pushButton];
/* 初始化各种数据 */
self.animation = [[CXMoveNavigationTransitionAnimation alloc] init];
self.interactionController = [[UIPercentDrivenInteractiveTransition alloc] init];
[self.navigationController.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)]];
}
- (void)push
{
CXToMoveNVC* toView = [[CXToMoveNVC alloc] init];
self.navigationController.delegate = self;
[self.navigationController pushViewController:toView animated:YES];
}
#pragma mark -- UIGestureRecognizerDelegate
-(void)handleGesture:(UIPanGestureRecognizer *)gesture {
UIView* view = self.navigationController.view;
CGPoint location = [gesture locationInView:gesture.view];
CGPoint translation = [gesture translationInView:gesture.view];
switch (gesture.state) {
case UIGestureRecognizerStateBegan: {
_interActiving = YES;
if (location.x < CGRectGetMidX(view.bounds) && self.navigationController.viewControllers.count > 1) {
[self.navigationController popViewControllerAnimated:YES];
}
break;
}
case UIGestureRecognizerStateChanged: {
CGFloat fraction = fabs(translation.x / view.bounds.size.width);
[_interactionController updateInteractiveTransition:fraction];
break;
}
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded: {
_interActiving = NO;
CGFloat fraction = fabs(translation.x / view.bounds.size.width);
if (fraction < 0.5 || [gesture velocityInView:view].x < 0 || gesture.state == UIGestureRecognizerStateCancelled) {
[_interactionController cancelInteractiveTransition];
} else {
[_interactionController finishInteractiveTransition];
}
break;
}
default:
break;
}
}
#pragma mark -- UINavigationControllerDelegate
- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
if (operation == UINavigationControllerOperationPop) {
return _animation;
}
return nil;
}
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController
{
return self.interActiving ? self.interactionController : nil;
}
@end


4、运行查看动画效果

最后

在iOS7之后导航控制器自带了一个交互式切换动画,我们只要从屏幕左侧右滑就能回到上一层,我们可以自定义这个手势是否开启(默认开启)

self.navigationController.interactivePopGestureRecognizer.enabled = YES;


有时候我们定义了leftBarButtonItem后这个手势往往会失效。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: