UIVIewController自定义切换效果-b
?
?
之前介紹動(dòng)畫時(shí)提過UIView的轉(zhuǎn)場(chǎng)動(dòng)畫,但是開發(fā)中我們碰到更多的viewController的切換,ios中常見的viewcontroller切換有四種:模態(tài)視圖,導(dǎo)航欄控制器,UITabBarController以及addchildviewcontroller,自定義viewcontroller動(dòng)畫切換也是ios7中的新特性,這里整理下常見的操作,outline如下(本文參考http://onevcat.com/2013/10/vc-transition-in-ios7/,代碼下載地址為https://github.com/zanglitao/UIVIewControllerSwitch)
1:基本介紹
2:模態(tài)視圖自定義動(dòng)畫切換
3:UINavigationController自定義動(dòng)畫切換
4:UITaBarController自定義動(dòng)畫切換
5:模態(tài)視圖交互式動(dòng)畫切換
6:UINavigationController交互式動(dòng)畫切換
?
基本介紹
在深入之前,我們先來看看新SDK中有關(guān)這部分內(nèi)容的相關(guān)接口以及它們的關(guān)系和典型用法。這幾個(gè)接口和類的名字都比較相似,但是還是能比較好的描述出各自的職能的,一開始的話可能比較迷惑,但是當(dāng)自己動(dòng)手實(shí)現(xiàn)一兩個(gè)例子之后,它們之間的關(guān)系就會(huì)逐漸明晰起來。(相關(guān)的內(nèi)容都定義在UIKit的UIViewControllerTransitioning.h中了)
@protocol?UIViewControllerContextTransitioning
這個(gè)接口用來提供切換上下文給開發(fā)者使用,包含了從哪個(gè)VC到哪個(gè)VC等各類信息,一般不需要開發(fā)者自己實(shí)現(xiàn)。具體來說,iOS7的自定義切換目的之一就是切換相關(guān)代碼解耦,在進(jìn)行VC切換時(shí),做切換效果實(shí)現(xiàn)的時(shí)候必須要需要切換前后VC的一些信息,系統(tǒng)在新加入的API的比較的地方都會(huì)提供一個(gè)實(shí)現(xiàn)了該接口的對(duì)象,以供我們使用。
對(duì)于切換的動(dòng)畫實(shí)現(xiàn)來說(這里先介紹簡(jiǎn)單的動(dòng)畫,在后面我會(huì)再引入手勢(shì)驅(qū)動(dòng)的動(dòng)畫),這個(gè)接口中最重要的方法有:
- -(UIView *)containerView; VC切換所發(fā)生的view容器,開發(fā)者應(yīng)該將切出的view移除,將切入的view加入到該view容器中。
- -(UIViewController *)viewControllerForKey:(NSString *)key; 提供一個(gè)key,返回對(duì)應(yīng)的VC。現(xiàn)在的SDK中key的選擇只有UITransitionContextFromViewControllerKey和UITransitionContextToViewControllerKey兩種,分別表示將要切出和切入的VC。
- -(CGRect)initialFrameForViewController:(UIViewController *)vc; 某個(gè)VC的初始位置,可以用來做動(dòng)畫的計(jì)算。
- -(CGRect)finalFrameForViewController:(UIViewController *)vc; 與上面的方法對(duì)應(yīng),得到切換結(jié)束時(shí)某個(gè)VC應(yīng)在的frame。
- -(void)completeTransition:(BOOL)didComplete; 向這個(gè)context報(bào)告切換已經(jīng)完成。
@protocol?UIViewControllerAnimatedTransitioning
這個(gè)接口負(fù)責(zé)切換的具體內(nèi)容,也即“切換中應(yīng)該發(fā)生什么”。開發(fā)者在做自定義切換效果時(shí)大部分代碼會(huì)是用來實(shí)現(xiàn)這個(gè)接口。它只有兩個(gè)方法需要我們實(shí)現(xiàn):
-
-(NSTimeInterval)transitionDuration:(id < UIViewControllerContextTransitioning >)transitionContext; 系統(tǒng)給出一個(gè)切換上下文,我們根據(jù)上下文環(huán)境返回這個(gè)切換所需要的花費(fèi)時(shí)間(一般就返回動(dòng)畫的時(shí)間就好了,SDK會(huì)用這個(gè)時(shí)間來在百分比驅(qū)動(dòng)的切換中進(jìn)行幀的計(jì)算,后面再詳細(xì)展開)。
-
-(void)animateTransition:(id < UIViewControllerContextTransitioning >)transitionContext; 在進(jìn)行切換的時(shí)候?qū)⒄{(diào)用該方法,我們對(duì)于切換時(shí)的UIView的設(shè)置和動(dòng)畫都在這個(gè)方法中完成。
@protocol?UIViewControllerTransitioningDelegate
這個(gè)接口的作用比較簡(jiǎn)單單一,在需要VC切換的時(shí)候系統(tǒng)會(huì)像實(shí)現(xiàn)了這個(gè)接口的對(duì)象詢問是否需要使用自定義的切換效果。這個(gè)接口共有四個(gè)類似的方法:
-
-(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;
前兩個(gè)方法是針對(duì)動(dòng)畫切換的,我們需要分別在呈現(xiàn)VC和解散VC時(shí),給出一個(gè)實(shí)現(xiàn)了UIViewControllerAnimatedTransitioning接口的對(duì)象(其中包含切換時(shí)長(zhǎng)和如何切換)。后兩個(gè)方法涉及交互式切換,之后再說。
??
模態(tài)視圖自定義動(dòng)畫
?1:建立一個(gè)模態(tài)視圖控制器
//ModalViewController.h @class ModalViewController; @protocol ModalViewControllerDelegate <NSObject> -(void)dismissViewController:(ModalViewController *)mcv; @end@interface ModalViewController : UIViewController @property(nonatomic,weak)id<ModalViewControllerDelegate> delegate; @end//ModalViewController.m @interface ModalViewController ()@end@implementation ModalViewController- (void)viewDidLoad {[super viewDidLoad];[self.view setBackgroundColor:[UIColor greenColor]];// Do any additional setup after loading the view.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:建立主視圖控制器,實(shí)現(xiàn)ModalViewControllerDelegate協(xié)議
//ViewController.h @interface ViewController : UIViewController<ModalViewControllerDelegate>@end//ViewController.m @implementation ViewController- (void)viewDidLoad {[super viewDidLoad];UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];[button addTarget:self action:@selector(pushViewController) forControlEvents:UIControlEventTouchUpInside];[button setTitle:@"push" forState:UIControlStateNormal];[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];[button setFrame:CGRectMake(0, 0, 130, 200)];[self.view addSubview:button]; }-(void)pushViewController {ModalViewController *controller = [[ModalViewController alloc] init];controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical;controller.delegate = self;[self presentViewController:controller animated:YES completion:nil]; }-(void)dismissViewController:(ModalViewController *)mcv {[self dismissViewControllerAnimated:YES completion:nil]; }@end?
上面的代碼實(shí)現(xiàn)了模態(tài)視圖的切換,通過?controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical還可以設(shè)置切換的動(dòng)畫效果,ios內(nèi)置了幾種切換效果供開發(fā)者使用,但是我們現(xiàn)在需要自定義動(dòng)畫效果
3:新建一個(gè)類實(shí)現(xiàn)UIViewControllerAnimatedTransitioning協(xié)議,這個(gè)類就是我們的動(dòng)畫切換類,ios7實(shí)現(xiàn)了動(dòng)畫切換類與試圖控制器類的解耦,編寫一個(gè)動(dòng)畫切換類可以反復(fù)重用
//ModalTransitionAnimation.h @interface ModalTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning> @end//ModalTransitionAnimation.m @implementation ModalTransitionAnimation//動(dòng)畫持續(xù)時(shí)間,單位是秒 - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {return 1; }//動(dòng)畫效果 - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {//通過鍵值UITransitionContextToViewControllerKey獲取需要呈現(xiàn)的視圖控制器toVCUIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];//得到toVC完全呈現(xiàn)后的frameCGRect finalFrame = [transitionContext finalFrameForViewController:toVC];if ([toVC isKindOfClass:[ModalViewController class]]) {//需要呈現(xiàn)的視圖是模態(tài)視圖,此時(shí)將模態(tài)視圖的frame放到屏幕空間下方,這樣才能實(shí)現(xiàn)從下方彈出的效果toVC.view.frame = CGRectOffset(finalFrame, 0, [UIScreen mainScreen].bounds.size.height);} else {//需要呈現(xiàn)的視圖是主視圖,此時(shí)將主視圖的frame放在屏幕空間上方,這樣才能實(shí)現(xiàn)從上方放下的效果toVC.view.frame = CGRectOffset(finalFrame, 0, -[UIScreen mainScreen].bounds.size.height);}//切換在containerView中完成,需要將toVC.view加到containerView中UIView *containerView = [transitionContext containerView];[containerView addSubview:toVC.view];//開始動(dòng)畫,這里使用了UIKit提供的彈簧效果動(dòng)畫,usingSpringWithDamping越接近1彈性效果越不明顯,此API在IOS7之后才能使用[UIView animateWithDuration:[self transitionDuration:transitionContext]delay:0usingSpringWithDamping:0.6initialSpringVelocity:0options:UIViewAnimationOptionCurveEaseInanimations:^{toVC.view.frame = finalFrame;} completion:^(BOOL finished) {//通知系統(tǒng)動(dòng)畫切換完成[transitionContext completeTransition:YES];}]; }@end?
上面的代碼實(shí)現(xiàn)了從屏幕下方彈性彈出ModalViewController以及將ModalViewController彈回屏幕下方的動(dòng)畫效果,上面代碼[toVC isKindOfClass:[ModalViewController class]]將動(dòng)畫切換類與視圖控制器耦合了起來,實(shí)際開發(fā)中不是一種好的方式,此處僅僅為了演示
4:重新配置主視圖控制器,使用我們自定義的動(dòng)畫
//ViewController.h //如果需要使用自定義動(dòng)畫,視圖需要實(shí)現(xiàn)UIViewControllerTransitioningDelegate協(xié)議 @interface ViewController : UIViewController<ModalViewControllerDelegate,UIViewControllerTransitioningDelegate>@end//ViewController.m @interface ViewController () @property(nonatomic,strong)ModalTransitionAnimation *animation; @end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib._animation = [[ModalTransitionAnimation alloc] init];UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];[button addTarget:self action:@selector(pushViewController) forControlEvents:UIControlEventTouchUpInside];[button setTitle:@"push" forState:UIControlStateNormal];[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];[button setFrame:CGRectMake(0, 0, 130, 200)];[self.view addSubview:button]; }-(void)pushViewController {ModalViewController *controller = [[ModalViewController alloc] init];controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical;controller.delegate = self;controller.transitioningDelegate = self; [self presentViewController:controller animated:YES completion:nil];}-(void)dismissViewController:(ModalViewController *)mcv {[self dismissViewControllerAnimated:YES completion:nil]; }- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {return _animation; }- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {return _animation;}@end?
我們?cè)O(shè)置了模態(tài)視圖控制器的transitioningDelegate為self,當(dāng)present和dismiss模態(tài)視圖時(shí)系統(tǒng)會(huì)像實(shí)現(xiàn)了這個(gè)接口的對(duì)象詢問是否需要使用自定義的切換效果
這個(gè)接口共有四個(gè)類似的方法:
-
-(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;
我們實(shí)現(xiàn)了前兩個(gè)方法用來設(shè)置模態(tài)視圖出現(xiàn)和消失的動(dòng)畫,后兩個(gè)方法用來處理交互式動(dòng)畫,后面會(huì)提到使用方法。
5:運(yùn)行代碼,可以看到模態(tài)視圖自定義的動(dòng)畫效果
?
UINavigationController自定義動(dòng)畫切換
除了使用模態(tài)視圖,導(dǎo)航欄控制器也是使用最多最常見的視圖切換方式,我們也可以自定義導(dǎo)航欄控制器的動(dòng)畫
1:通過storyboard建立一個(gè)導(dǎo)航欄控制器
?
此時(shí)運(yùn)行程序我們就有兩個(gè)視圖控制器可以切換(storyboard真心強(qiáng)大,特別ios8引入sizeclass后強(qiáng)烈建議使用storyboard)?
?
2:建立我們的動(dòng)畫切換類
//NavigationTransitionAnimation.h @interface NavigationTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning>@end//NavigationTransitionAnimation.m @implementation NavigationTransitionAnimation//動(dòng)畫持續(xù)時(shí)間0.7秒 - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {return 0.7; }- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {//通過鍵值UITransitionContextToViewControllerKey獲得需要呈現(xiàn)的試圖控制器UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];//通過鍵值UITransitionContextFromViewControllerKey獲得需要退出的試圖控制器UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];[[transitionContext containerView] addSubview:toVC.view];//設(shè)置需要呈現(xiàn)的試圖控制器透明[toVC.view setAlpha:0];//設(shè)置需要呈現(xiàn)的試圖控制器位于左側(cè)屏幕外,且大小為0.1倍,這樣才有從左側(cè)推入屏幕,且逐漸變大的動(dòng)畫效果toVC.view.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(-[UIScreen mainScreen].bounds.size.width, 0), 0.1, 0.1);[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{//將需要退出的試圖控制器移出右側(cè)屏幕外,且大小為原來的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) {//動(dòng)畫結(jié)束后屬性設(shè)為初始值fromVC.view.transform = CGAffineTransformIdentity;fromVC.view.alpha = 1;//通知系統(tǒng)動(dòng)畫切換成功[transitionContext completeTransition:YES];}]; } @end?
3:為導(dǎo)航欄控制器添加動(dòng)畫效果,之前為模態(tài)視圖設(shè)置自定義動(dòng)畫時(shí)有個(gè)協(xié)議UIViewControllerTransitioningDelegate,只有設(shè)置了協(xié)議且實(shí)現(xiàn)了協(xié)議方法,那么模態(tài)視圖切換時(shí)會(huì)使用設(shè)置的自定義動(dòng)畫,在導(dǎo)航欄控制器中同樣有一個(gè)協(xié)議定義了一系列方法用來切換視圖控制器時(shí)詢問是否使用自定義方法,那就是UINavigationControllerDelegate
//NavigationControllerDelegate.h @interface NavigationControllerDelegate : NSObject<UINavigationControllerDelegate>@end//NavigationControllerDelegate.m @interface NavigationControllerDelegate()@property (weak, nonatomic) IBOutlet UINavigationController *navigationController; @property(nonatomic,retain)NavigationTransitionAnimation *animation; @end@implementation NavigationControllerDelegate-(void)awakeFromNib {self.animation = [[NavigationTransitionAnimation alloc] init]; }- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationControlleranimationControllerForOperation:(UINavigationControllerOperation)operationfromViewController:(UIViewController *)fromVCtoViewController:(UIViewController *)toVC {if (operation == UINavigationControllerOperationPop) {return _animation;}return nil; }@end?上面代碼只在pop時(shí)使用了自定義動(dòng)畫,當(dāng)然也可以在push和pop時(shí)均使用自定義動(dòng)畫,并且可以為兩種操作使用不同的自定義動(dòng)畫
UINavigationControllerDelegate中有兩個(gè)方法與視圖控制器切換動(dòng)畫相關(guān)
- (id?<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController*)navigationController?interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController?
- (id?<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController*)navigationController?animationControllerForOperation:(UINavigationControllerOperation)operation?fromViewController:(UIViewController *)fromVC?toViewController:(UIViewController *)toVC
其中第一個(gè)方法用于交互式動(dòng)畫
?
4:連結(jié)@property (weak, nonatomic) IBOutlet UINavigationController *navigationController和storyboard中的導(dǎo)航欄控制器,并設(shè)置導(dǎo)航欄控制器的delegate
在storyboard中設(shè)置delegate的步驟如下:
首先選擇object
然后將object拖到navigationcontroller上
然后設(shè)置class
最后與navigationcontroller的delegate連結(jié)
?
5:運(yùn)行代碼,可以看到導(dǎo)航欄控制器自定義的動(dòng)畫效果
?
UITabBarController自定義動(dòng)畫切換
?接下來我們來看第三種常見的試圖控制器切換方法:UITabBarController
?1:通過storyboard建立一個(gè)UITabBarController
?
?
?
2:建立我們的動(dòng)畫切換類
//TabbarTransitionAnimation.h @interface TabbarTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning>@end//TabbarTransitionAnimation.m #define PERSPECTIVE -1.0/200 @implementation TabbarTransitionAnimation- (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;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?
以上代碼實(shí)現(xiàn)了矩形切換的動(dòng)畫效果
3:為UITabBarController添加動(dòng)畫效果,相關(guān)的協(xié)議是UITabBarControllerDelegate
//TabbarControllerDelegate.h @interface TabbarControllerDelegate : NSObject<UITabBarControllerDelegate>@end//TabbarControllerDelegate.m @interface TabbarControllerDelegate() @property (weak, nonatomic) IBOutlet UITabBarController *tabbarController; @property(nonatomic,strong)TabbarTransitionAnimation *animation; @end@implementation TabbarControllerDelegate- (void)awakeFromNib {_animation = [[TabbarTransitionAnimation alloc] init]; }- (id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarControlleranimationControllerForTransitionFromViewController:(UIViewController *)fromVCtoViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0) {return _animation; } @end4:連結(jié)@property (weak, nonatomic) IBOutletUITabBarController *tabbarController和storyboard中的UITabBarController,并設(shè)置UITabBarController的delegate(方式和導(dǎo)航欄控制器一致)
5:運(yùn)行代碼,可以看到UITabBarController自定義的動(dòng)畫效果
?
模態(tài)視圖交互式動(dòng)畫切換
前面所有的切換都是當(dāng)點(diǎn)擊完一個(gè)按鈕后就立刻執(zhí)行,但是有時(shí)候我們希望某些切換操作可以進(jìn)行到一半取消,比如我們?yōu)榍耙粋€(gè)模態(tài)視圖切換提供手勢(shì)支持,隨著手勢(shì)向下拉,模態(tài)視圖慢慢退出屏幕底部,但當(dāng)我們拉到一半取消或者向上拉,模態(tài)視圖就會(huì)回到之前的狀態(tài),這就是所謂的交互式切換
我們?cè)谇耙粋€(gè)例子的基礎(chǔ)上添加交互式動(dòng)畫切換
1:動(dòng)畫效果類(完全可以使用之前的動(dòng)畫效果類,這里為了說明自定義交互式動(dòng)畫的流程所以重新寫一個(gè)自定義動(dòng)畫)
//ModalMoveTransitionAnimation.h @interface ModalMoveTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning>@end//ModalMoveTransitionAnimation.m @implementation ModalMoveTransitionAnimation- (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;} completion:^(BOOL finished) {[transitionContext completeTransition:![transitionContext transitionWasCancelled]];}];} @end?
?上面代碼中值得注意的是最后[transitionContext completeTransition:![transitionContext transitionWasCancelled]];因?yàn)樵诮换ナ角袚Q中切換可能會(huì)取消,所以這里使用[transitionContext transitionWasCancelled]判斷切換是否成功
2:添加手勢(shì)
首先我們需要在剛才的知識(shí)基礎(chǔ)上補(bǔ)充一些東西:
首先是UIViewControllerContextTransitioning,剛才提到這個(gè)是系統(tǒng)提供的VC切換上下文,如果您深入看了它的頭文件描述的話,應(yīng)該會(huì)發(fā)現(xiàn)其中有三個(gè)關(guān)于InteractiveTransition的方法,正是用來處理交互式切換的。但是在初級(jí)的實(shí)際使用中我們其實(shí)可以不太理會(huì)它們,而是使用iOS 7 SDK已經(jīng)給我們準(zhǔn)備好的一個(gè)現(xiàn)成轉(zhuǎn)為交互式切換而新加的類:UIPercentDrivenInteractiveTransition。
UIPercentDrivenInteractiveTransition是什么
這是一個(gè)實(shí)現(xiàn)了UIViewControllerInteractiveTransitioning接口的類,為我們預(yù)先實(shí)現(xiàn)和提供了一系列便利的方法,可以用一個(gè)百分比來控制交互式切換的過程。一般來說我們更多地會(huì)使用某些手勢(shì)來完成交互式的轉(zhuǎn)移(當(dāng)然用的高級(jí)的話用其他的輸入..比如聲音,iBeacon距離或者甚至面部微笑來做輸入驅(qū)動(dòng)也無不可,畢竟想象無極限嘛..),這樣使用這個(gè)類(一般是其子類)的話就會(huì)非常方便。我們?cè)谑謩?shì)識(shí)別中只需要告訴這個(gè)類的實(shí)例當(dāng)前的狀態(tài)百分比如何,系統(tǒng)便根據(jù)這個(gè)百分比和我們之前設(shè)定的遷移方式為我們計(jì)算當(dāng)前應(yīng)該的UI渲染,十分方便。具體的幾個(gè)重要方法:
- -(void)updateInteractiveTransition:(CGFloat)percentComplete 更新百分比,一般通過手勢(shì)識(shí)別的長(zhǎng)度之類的來計(jì)算一個(gè)值,然后進(jìn)行更新。之后的例子里會(huì)看到詳細(xì)的用法
- -(void)cancelInteractiveTransition 報(bào)告交互取消,返回切換前的狀態(tài)
- –(void)finishInteractiveTransition 報(bào)告交互完成,更新到切換后的狀態(tài)
@protocol?UIViewControllerInteractiveTransitioning
就如上面提到的,UIPercentDrivenInteractiveTransition只是實(shí)現(xiàn)了這個(gè)接口的一個(gè)類。為了實(shí)現(xiàn)交互式切換的功能,我們需要實(shí)現(xiàn)這個(gè)接口。因?yàn)榇蟛糠謺r(shí)候我們其實(shí)不需要自己來實(shí)現(xiàn)這個(gè)接口,因此在這篇入門中就不展開說明了,有興趣的童鞋可以自行鉆研。
還有就是上面提到過的UIViewControllerTransitioningDelegate中的返回Interactive實(shí)現(xiàn)對(duì)象的方法,我們同樣會(huì)在交互式切換中用到它們。
?
UIPercentDrivenInteractiveTransition可以直接拿來使用,也可以繼承它,這里我們繼承它
//ModalInterActiveTransitionAnimation.h @interface ModalInterActiveTransitionAnimation : UIPercentDrivenInteractiveTransition@property(nonatomic,assign)BOOL interacting; - (void)wireToViewController:(UIViewController*)viewController;@end//ModalInterActiveTransitionAnimation.m @interface ModalInterActiveTransitionAnimation()@property (nonatomic, strong) UIViewController *presentingVC; @property (nonatomic, assign) BOOL shouldComplete;@end@implementation ModalInterActiveTransitionAnimation- (void)wireToViewController:(UIViewController*)viewController{_presentingVC = viewController;//添加手勢(shì)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);[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用來判斷當(dāng)前是否處于交互式視圖切換過程中,只有處在這個(gè)過程中我們才需要在
- (id?<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator
使用動(dòng)畫
?
3:添加交互式動(dòng)畫切換
//ViewController.m @interface ViewController ()@property(nonatomic,strong)ModalTransitionAnimation *animation; @property(nonatomic,strong)ModalInterActiveTransitionAnimation *interActive; @property(nonatomic,strong)ModalMoveTransitionAnimation *interActiveAnimation; @end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib._animation = [[ModalTransitionAnimation alloc] init];_interActive = [[ModalInterActiveTransitionAnimation alloc] init];_interActiveAnimation = [[ModalMoveTransitionAnimation alloc] init];UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];[button addTarget:self action:@selector(pushViewController) forControlEvents:UIControlEventTouchUpInside];[button setTitle:@"push" forState:UIControlStateNormal];[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];[button setFrame:CGRectMake(0, 0, 130, 200)];[self.view addSubview:button]; }-(void)pushViewController {ModalViewController *controller = [[ModalViewController alloc] init];controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical;controller.delegate = self;controller.transitioningDelegate = self;[_interActive wireToViewController:controller];[self presentViewController:controller animated:YES completion:nil];}-(void)dismissViewController:(ModalViewController *)mcv {[self dismissViewControllerAnimated:YES completion:nil]; }- (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?
回顧上面的流程,我們?yōu)関iew添加手勢(shì),然后在手勢(shì)中根據(jù)情況調(diào)用UIPercentDrivenInteractiveTransition的三個(gè)方法(updateInteractiveTransition,cancelInteractiveTransition,finishInteractiveTransition),最后在interactionControllerForDismissal中返回我們定義的動(dòng)畫,這樣就可以實(shí)現(xiàn)交互式動(dòng)畫切換了
?
UINavigationController交互式動(dòng)畫切換
?與上面的流程相似
1:添加手勢(shì),在手勢(shì)中調(diào)用UIPercentDrivenInteractiveTransition的三個(gè)方法
2:在相應(yīng)的代理方法中返回我們定義的動(dòng)畫
這里我們不用繼承UIPercentDrivenInteractiveTransition,而是直接使用它,同時(shí)動(dòng)畫也直接使用之前在導(dǎo)航欄控制器切換中定義好的動(dòng)畫,不過代碼需要稍加修改
//NavigationTransitionAnimation.m @implementation NavigationTransitionAnimation//動(dòng)畫持續(xù)時(shí)間0.7秒 - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {return 0.7; }- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {//通過鍵值UITransitionContextToViewControllerKey獲得需要呈現(xiàn)的試圖控制器UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];//通過鍵值UITransitionContextFromViewControllerKey獲得需要退出的試圖控制器UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];[[transitionContext containerView] addSubview:toVC.view];//設(shè)置需要呈現(xiàn)的試圖控制器透明[toVC.view setAlpha:0];//設(shè)置需要呈現(xiàn)的試圖控制器位于左側(cè)屏幕外,且大小為0.1倍,這樣才有從左側(cè)推入屏幕,且逐漸變大的動(dòng)畫效果toVC.view.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(-[UIScreen mainScreen].bounds.size.width, 0), 0.1, 0.1);[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{//將需要退出的試圖控制器移出右側(cè)屏幕外,且大小為原來的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) {//動(dòng)畫結(jié)束后屬性設(shè)為初始值fromVC.view.transform = CGAffineTransformIdentity;fromVC.view.alpha = 1;toVC.view.transform = CGAffineTransformIdentity;toVC.view.alpha = 1;[transitionContext completeTransition:![transitionContext transitionWasCancelled]];}]; } @end我們修改了最后幾行代碼,讓它適應(yīng)交互式切換
?
添加我們的手勢(shì)以及自定義交互動(dòng)畫
@interface NavigationControllerDelegate()@property (weak, nonatomic) IBOutlet UINavigationController *navigationController; @property(nonatomic,retain)NavigationTransitionAnimation *animation; @property (strong, nonatomic) UIPercentDrivenInteractiveTransition* interactionController; @property (nonatomic,assign)BOOL interActiving; @end@implementation NavigationControllerDelegate-(void)awakeFromNib {self.animation = [[NavigationTransitionAnimation alloc] init];self.interactionController = [[UIPercentDrivenInteractiveTransition alloc] init];[self.navigationController.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)]]; }-(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;} }- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationControlleranimationControllerForOperation:(UINavigationControllerOperation)operationfromViewController:(UIViewController *)fromVCtoViewController:(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?
最后
在ios7之后導(dǎo)航欄控制器自帶了一個(gè)交互式切換動(dòng)畫,我們只要從屏幕左側(cè)向右滑就能回到上一層
我們可以自定義這個(gè)手勢(shì)是否開啟(默認(rèn)開啟)
self.navigationController.interactivePopGestureRecognizer.enabled =?YES;
?
但當(dāng)我們定義了leftBarButtonItem后這個(gè)手勢(shì)往往會(huì)失效,解決方法可以看這篇博客
總結(jié)
以上是生活随笔為你收集整理的UIVIewController自定义切换效果-b的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据库SQL及相关
- 下一篇: springMVC - 之拦截器