前言
最近在研究定制viewController轉(zhuǎn)場(chǎng)诬滩,看了一篇github上的文章覺得寫得很透徹(原文)疼鸟,是學(xué)習(xí)viewController轉(zhuǎn)場(chǎng)的絕佳教材。這里只是做一些筆記浩淘,一方面加深理解张抄,另一方面是將來如果忘記了可以回頭看看署惯。
視圖控制器中的view顯示在屏幕上有兩種方式:最主要的方式是內(nèi)嵌在容器控制器中极谊,比如UINavigationController安岂,UITabBarController, UISplitController域那。很多app都是UITabBarController容器裝了N個(gè)UINavigationController然后每個(gè)UINavigationController里面又裝了N個(gè)UIViewController琉雳。另外一種顯示方式就是一個(gè)UIViewController控制顯示另外一個(gè)UIViewController這總方式叫做模態(tài)顯示(Modal);
轉(zhuǎn)場(chǎng)是啥玩意兒:轉(zhuǎn)場(chǎng)是下一場(chǎng)景(子VC)的視圖替換當(dāng)前場(chǎng)景(子視圖)以及相應(yīng)的控制器的替換翠肘,表現(xiàn)為當(dāng)前視圖消失束倍,下一視圖出現(xiàn).像在UINavigationController 容器中push或者pop一個(gè)UIViewController或者tabbarController點(diǎn)擊tab切換當(dāng)前顯示的controller绪妹。iOS7之后系統(tǒng)提供了控制viewController轉(zhuǎn)場(chǎng)的api讓我們可以定制自己的轉(zhuǎn)廠動(dòng)畫柿究,實(shí)現(xiàn)交互式轉(zhuǎn)場(chǎng)和為自定義容器添加轉(zhuǎn)場(chǎng)等蝇摸。以前只能使用系統(tǒng)默認(rèn)的轉(zhuǎn)場(chǎng).轉(zhuǎn)場(chǎng)可以分為兩種類型,非交互轉(zhuǎn)場(chǎng)民镜,交互式轉(zhuǎn)場(chǎng)制圈。如push就是一個(gè)非交互轉(zhuǎn)場(chǎng),而系統(tǒng)自帶那個(gè)從屏幕左邊緣滑動(dòng)返回是一個(gè)交互式轉(zhuǎn)場(chǎng)鲸鹦,因?yàn)槲覀兛梢酝ㄟ^手勢(shì)去影響轉(zhuǎn)場(chǎng)過程和進(jìn)度亥鬓。更準(zhǔn)確的說是交互式轉(zhuǎn)場(chǎng)依賴于手勢(shì)等某種交互方式來完成專場(chǎng)過程域庇。
下面看看如何去定制自己的轉(zhuǎn)場(chǎng)听皿。
uikit通過協(xié)議的方式讓我們?nèi)ザㄖ妻D(zhuǎn)場(chǎng)尉姨,定制轉(zhuǎn)場(chǎng)時(shí)候需要做的就是提供實(shí)現(xiàn)了這些協(xié)議的對(duì)象又厉。其實(shí)最最核心的就是提供一個(gè)轉(zhuǎn)場(chǎng)動(dòng)畫.
實(shí)現(xiàn)自定義轉(zhuǎn)場(chǎng)的步驟:
1.提供一個(gè)轉(zhuǎn)場(chǎng)代理覆致,
告訴系統(tǒng)使用我們提供的代理而非默認(rèn)煌妈。具體來說就是提供實(shí)現(xiàn)了UINavigationControllerDelegate璧诵,UITabBarControllerDelegate或者UIViewControllerTransitioningDelegate協(xié)議的對(duì)象。要定制的轉(zhuǎn)場(chǎng)屬于哪個(gè)容器的就提供一個(gè)對(duì)應(yīng)的對(duì)象族操。進(jìn)一步解釋就是:當(dāng)用戶通過操作出發(fā)了UINavigationController或者UITabBarController轉(zhuǎn)場(chǎng)的時(shí)候(如push色难,pop),容器控制器會(huì)去尋找代理對(duì)象莱预,然后代理會(huì)按照協(xié)議提供各種轉(zhuǎn)場(chǎng)需要的對(duì)象.動(dòng)畫控制器依沮,交互控制器等..
2.動(dòng)畫控制器
這是定制轉(zhuǎn)場(chǎng)中最重要的部分,負(fù)責(zé)執(zhí)行動(dòng)畫危喉。遵守UIViewControllerAnimatedTransitioning協(xié)議辜限,我們實(shí)現(xiàn)協(xié)議的方法來提供動(dòng)畫薄嫡。
3.交互控制器
當(dāng)要實(shí)現(xiàn)交互式轉(zhuǎn)場(chǎng)的時(shí)候要提供一個(gè)實(shí)現(xiàn)了UIViewControllerInteractiveTransitioning協(xié)議的對(duì)象提毫深,供交互控制哑蔫。系統(tǒng)已經(jīng)打包了一個(gè)現(xiàn)成的類UIPercentDrivenInteractiveTransition供我們使用闸迷。
4.轉(zhuǎn)場(chǎng)環(huán)境
遵守UIViewControllerContextTransitioning協(xié)議提供轉(zhuǎn)場(chǎng)中需要的數(shù)據(jù)腥沽,這個(gè)對(duì)象由UIKit在轉(zhuǎn)場(chǎng)開始前提供給前面的及動(dòng)畫控制器巡球,和交互控制器使用酣栈。
5.轉(zhuǎn)場(chǎng)協(xié)調(diào)器
遵守UIViewControllerTransitionCoordinator協(xié)議矿筝,可在轉(zhuǎn)場(chǎng)動(dòng)畫發(fā)生的同時(shí)并行執(zhí)行其他的動(dòng)畫棚贾,其作用與其說協(xié)調(diào)不如說輔助。
上面五種協(xié)議只需要關(guān)心前三個(gè)鼻疮。
demo1:定制UINavigationController轉(zhuǎn)場(chǎng)
UINavigationController轉(zhuǎn)場(chǎng)的最簡(jiǎn)實(shí)現(xiàn)只要提供一個(gè)遵守UINavigationControllerDelegate的對(duì)象和一個(gè)遵守UIViewControllerAnimatedTransitioning協(xié)議的對(duì)象即可,下面上代碼.
代理類是這樣的:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface CustomNavigationDelegate : NSObject<UINavigationControllerDelegate>
@end
#import "CustomNavigationDelegate.h"
#import "CustomNavigationAnimatior.h"
@implementation CustomNavigationDelegate
//返回一個(gè)動(dòng)畫實(shí)現(xiàn)類
-(id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
CustomNavigationAnimatior *animatior = [CustomNavigationAnimatior new];
animatior.operation = operation;
return animatior;
}
@end
動(dòng)畫實(shí)現(xiàn)類是這樣的:
fromVC和toVC理解
假設(shè)有兩個(gè)ViewController A 和B 挪哄,當(dāng)由A轉(zhuǎn)到B的時(shí)候(A的視圖消失迹炼,B的視圖出現(xiàn))A就叫做fromVC另外一個(gè)B叫做toVC.比如從A push 到 B斯入。同理 當(dāng)由B pop 回A的時(shí)候 B是from咱扣,A是to闹伪。
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface CustomNavigationAnimatior : NSObject<UIViewControllerAnimatedTransitioning>
//push or pop
@property(assign,nonatomic)UINavigationControllerOperation operation;
@end
#import "CustomNavigationAnimatior.h"
@implementation CustomNavigationAnimatior
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.3;
}
//提供動(dòng)畫具體實(shí)現(xiàn)
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = transitionContext.containerView;
UIView *fromView = fromVC.view;
UIView *toView = toVC.view;
CGAffineTransform toViewTransform = CGAffineTransformIdentity;
CGAffineTransform fromViewTransform = CGAffineTransformIdentity;
CGFloat translation = containerView.frame.size.width;
if (self.operation == UINavigationControllerOperationPop) {
translation *= -1;
}
toViewTransform = CGAffineTransformMakeTranslation(translation, 0);
fromViewTransform = CGAffineTransformMakeTranslation(-translation, 0);
//注意:將toview添加到containerView
[containerView addSubview:toView];
toView.transform = toViewTransform;
//這個(gè)動(dòng)畫就是模仿系統(tǒng)的push和pop,這里重點(diǎn)是怎樣提供一個(gè)自定義的轉(zhuǎn)場(chǎng)動(dòng)畫厅克。
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromView.transform = fromViewTransform;
toView.transform = CGAffineTransformIdentity;
}completion:^(BOOL finished) {
fromView.transform = CGAffineTransformIdentity;
toView.transform = CGAffineTransformIdentity;
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
//動(dòng)畫結(jié)束時(shí)調(diào)用
-(void)animationEnded:(BOOL)transitionCompleted
{
}
然后只要實(shí)例化一個(gè)CustomNavigationDelegate作為navigationController的代理就OK了证舟,到此已經(jīng)替換掉了系統(tǒng)的轉(zhuǎn)場(chǎng)動(dòng)畫女责。注意這個(gè)對(duì)象在使用的時(shí)候不要被銷毀抵知,最好把他聲明成navigationController的strong類型的屬性刷喜。還有就是實(shí)現(xiàn)轉(zhuǎn)場(chǎng)動(dòng)畫時(shí)候要把toView添加到containerView上掖疮。
為上面的轉(zhuǎn)場(chǎng)添加交互浊闪,使其變?yōu)榻换ナ睫D(zhuǎn)場(chǎng)
前面說過要實(shí)現(xiàn)交互,在上面的基礎(chǔ)上要滿足兩個(gè)條件规揪,1.提供一個(gè)實(shí)現(xiàn)了UIViewControllerInteractiveTransitioning協(xié)議的交互控制器,2.添加一個(gè)交互方式猛铅,使通過交互能夠推動(dòng)轉(zhuǎn)場(chǎng)的進(jìn)行奸忽。
交互控制器
在CustomNavigationDelegate中實(shí)現(xiàn)interactionControllerForAnimationController方法
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface CustomNavigationDelegate : NSObject<UINavigationControllerDelegate>
//用系統(tǒng)打包好的交互控制器類
@property (nonatomic, strong)UIPercentDrivenInteractiveTransition *interactionController;
//控制是否開啟交互
@property (nonatomic, assign)BOOL interactive;
@end
#import "CustomNavigationDelegate.h"
#import "CustomNavigationAnimatior.h"
@implementation CustomNavigationDelegate
-(UIPercentDrivenInteractiveTransition *)interactionController
{
if (!_interactionController) {
_interactionController = [UIPercentDrivenInteractiveTransition new];
}
return _interactionController;
}
//返回一個(gè)動(dòng)畫實(shí)現(xiàn)類
-(id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
CustomNavigationAnimatior *animatior = [CustomNavigationAnimatior new];
animatior.operation = operation;
return animatior;
}
//返回交互控制器
-(id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController
{
//如果開啟了交互但是沒有動(dòng)作去推進(jìn)轉(zhuǎn)場(chǎng),試圖會(huì)卡住不動(dòng)欠雌,因?yàn)樵诘却鉼pdate轉(zhuǎn)場(chǎng)進(jìn)度但是又沒有動(dòng)作去推進(jìn)富俄。
//所以只有pop時(shí)候開啟交互(滑動(dòng)手勢(shì)可控制進(jìn)度)而咆,push不添加交互暴备。interactive就是用來判斷的
return self.interactive?self.interactionController:nil;
}
@end
添加交互手勢(shì)
模仿系統(tǒng)滑動(dòng)返回涯捻,添加一個(gè)手勢(shì)UIScreenEdgePanGestureRecognizer障癌。那么問題來了混弥,這個(gè)手勢(shì)加載哪里蝗拿“校肯定是要pop返回的那個(gè)vc的view上仓手,不可能為每一個(gè)要滑動(dòng)返回的vc重復(fù)添加手勢(shì)嗽冒,于是乎搞一個(gè)基類CustomBaseViewController在這里面判斷下當(dāng)前vc是否是容器最頂層VC添坊,不是則添加交互手勢(shì)贬蛙,上代碼:
#import <UIKit/UIKit.h>
@interface CustomBaseViewController : UIViewController
@end
#import "CustomBaseViewController.h"
#import "CustomNavigationDelegate.h"
@interface CustomBaseViewController ()
@property(nonatomic,strong)CustomNavigationDelegate *navDelegate;
@end
@implementation CustomBaseViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (self.navigationController.viewControllers.count > 1) {
//添加滑動(dòng)返回手勢(shì)
[self addEdgePanGes];
}
}
-(void)addEdgePanGes
{
UIScreenEdgePanGestureRecognizer *panGes = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(handleEdgePanGesture:)];
panGes.edges = UIRectEdgeLeft;
[self.view addGestureRecognizer:panGes];
}
-(void)handleEdgePanGesture:(UIScreenEdgePanGestureRecognizer *)panGes
{
CGFloat translationX = [panGes translationInView:self.view].x;
//計(jì)算百分比
CGFloat percent = fabs(translationX)/self.view.frame.size.width;
switch (panGes.state) {
case UIGestureRecognizerStateBegan:
{
_navDelegate = (CustomNavigationDelegate *)self.navigationController.delegate;
_navDelegate.interactive = YES;
//觸發(fā)轉(zhuǎn)場(chǎng)開始
[self.navigationController popViewControllerAnimated:YES];
}
break;
case UIGestureRecognizerStateChanged:
{
[_navDelegate.interactionController updateInteractiveTransition:percent];
}
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
{
if (percent>0.5) {
[_navDelegate.interactionController finishInteractiveTransition];
}else{
[_navDelegate.interactionController cancelInteractiveTransition];
}
_navDelegate.interactive = NO;
}
break;
default:
break;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
其中handleEdgePanGesture用來根據(jù)手勢(shì)交互更新轉(zhuǎn)場(chǎng)從而推進(jìn)轉(zhuǎn)場(chǎng)的進(jìn)行氛堕。主要依靠UIPercentDrivenInteractiveTransition類的三個(gè)方法:
//按百分比更新轉(zhuǎn)場(chǎng)進(jìn)度
- (void)updateInteractiveTransition:(CGFloat)percentComplete;
//取消轉(zhuǎn)場(chǎng)使其恢復(fù)到開始狀態(tài)
- (void)cancelInteractiveTransition;
//直接跳到轉(zhuǎn)場(chǎng)結(jié)束狀態(tài)
- (void)finishInteractiveTransition;
這個(gè)類是系統(tǒng)打包好的,他遵守UIViewControllerInteractiveTransitioning協(xié)議乱灵,必要的時(shí)候可以遵守這個(gè)協(xié)議自己去實(shí)現(xiàn)一個(gè)痛倚。
demo2:定制UITabBarController轉(zhuǎn)場(chǎng)
方法和demo1差不多,就是實(shí)現(xiàn)代理蝉稳,在代理中返回動(dòng)畫控制器耘戚,交互控制器收津。然后在添加一個(gè)用于交互的pan手勢(shì).
上代碼:
首先繼承UITabBarController創(chuàng)建個(gè)子類EDTabBarController我們要在這里面添加手勢(shì)和代理撞秋。
#import <UIKit/UIKit.h>
@interface EDTabBarController : UITabBarController
@end
#import "EDTabBarController.h"
#import "EDTabBarDelegate.h"
@interface EDTabBarController ()
@property(nonatomic,strong)EDTabBarDelegate *tabbarDelegate;
@end
@implementation EDTabBarController
- (void)viewDidLoad {
[super viewDidLoad];
//添加代理和手勢(shì)
self.delegate = self.tabbarDelegate;
UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePan:)];
[self.view addGestureRecognizer:panGes];
}
-(EDTabBarDelegate *)tabbarDelegate
{
if (!_tabbarDelegate) {
_tabbarDelegate = [EDTabBarDelegate new];
}
return _tabbarDelegate;
}
-(void)handlePan:(UIPanGestureRecognizer *)panGes
{
//計(jì)算進(jìn)度吻贿,然后開始舅列,更新帐要,結(jié)束或取消
CGFloat translationX = [panGes translationInView:self.view].x;
CGFloat percent = fabs(translationX)/self.view.frame.size.width;
switch (panGes.state) {
case UIGestureRecognizerStateBegan:
{
//通過設(shè)置selectedIndex觸發(fā)開始轉(zhuǎn)場(chǎng)(如何判斷應(yīng)該顯示前一個(gè)還是后一個(gè))
//velocityX的絕對(duì)值代表手指在view上的移動(dòng)速度榨惠,正負(fù)號(hào)代表方向冒冬。
//正:手指向右滑動(dòng)简烤,也就是要顯示比當(dāng)前index小的VC
//負(fù):與上面相反
CGFloat velocityX = [panGes velocityInView:self.view].x;
if (velocityX > 0) {
if (self.selectedIndex > 0) {
self.tabbarDelegate.interactive = YES;
self.selectedIndex -= 1;
}
}else{
if (self.selectedIndex < self.viewControllers.count - 1) {
self.tabbarDelegate.interactive = YES;
self.selectedIndex += 1;
}
}
}
break;
case UIGestureRecognizerStateChanged:
{
//更新轉(zhuǎn)場(chǎng)進(jìn)度
[self.tabbarDelegate.interactionController updateInteractiveTransition:percent];
}
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
{
self.tabbarDelegate.interactionController.completionSpeed = 0.99;
if (percent > 0.3) {
[self.tabbarDelegate.interactionController finishInteractiveTransition];
}else{
[self.tabbarDelegate.interactionController cancelInteractiveTransition];
}
self.tabbarDelegate.interactive = NO;
}
break;
default:
break;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
代理類
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface EDTabBarDelegate : NSObject<UITabBarControllerDelegate>
@property (nonatomic, strong)UIPercentDrivenInteractiveTransition *interactionController;
@property (nonatomic, assign)BOOL interactive;
@end
#import "EDTabBarDelegate.h"
#import "EDTabbarTransitionAnimator.h"
@implementation EDTabBarDelegate
//返回動(dòng)畫控制器
-(id<UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
NSInteger fromIndex = [tabBarController.viewControllers indexOfObject:fromVC];
NSInteger toIndex = [tabBarController.viewControllers indexOfObject:toVC];
TabOperationDirection operation = fromIndex > toIndex?Left:Right;
EDTabbarTransitionAnimator *animator = [EDTabbarTransitionAnimator new];
animator.operation = operation;
return animator;
}
//返回交互控制器
-(id<UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController
{
return self.interactive?self.interactionController:nil;
}
-(UIPercentDrivenInteractiveTransition *)interactionController
{
if (!_interactionController) {
_interactionController = [UIPercentDrivenInteractiveTransition new];
}
return _interactionController;
}
動(dòng)畫控制器
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger,TabOperationDirection){
Left = 0,//像左滾動(dòng)
Right //向右滾動(dòng)
};
@interface EDTabbarTransitionAnimator : NSObject<UIViewControllerAnimatedTransitioning>
@property (nonatomic, assign) TabOperationDirection operation;
@end
#import "EDTabbarTransitionAnimator.h"
@implementation EDTabbarTransitionAnimator
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.3;
}
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = transitionContext.containerView;
UIView *fromView = fromVC.view;
UIView *toView = toVC.view;
CGAffineTransform toViewTransform = CGAffineTransformIdentity;
CGAffineTransform fromViewTransform = CGAffineTransformIdentity;
CGFloat translation = containerView.frame.size.width;
if (self.operation == Left) {
translation *= -1;
}
toViewTransform = CGAffineTransformMakeTranslation(translation, 0);
fromViewTransform = CGAffineTransformMakeTranslation(-translation, 0);
//將toview添加到containerView
[containerView addSubview:toView];
//動(dòng)畫很簡(jiǎn)單 就是兩個(gè)視圖的平移
toView.transform = toViewTransform;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromView.transform = fromViewTransform;
toView.transform = CGAffineTransformIdentity;
}completion:^(BOOL finished) {
fromView.transform = CGAffineTransformIdentity;
toView.transform = CGAffineTransformIdentity;
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
-(void)animationEnded:(BOOL)transitionCompleted
{
//動(dòng)畫結(jié)束后調(diào)用該方法
}
@end
到此定制UITabBarController轉(zhuǎn)場(chǎng)完成引瀑,和定制UINavigationController轉(zhuǎn)場(chǎng)差不多憨栽。
demo3:定制Modal轉(zhuǎn)場(chǎng)
modal轉(zhuǎn)場(chǎng)就是persent和didmiss轉(zhuǎn)場(chǎng)屑柔。Modal 轉(zhuǎn)場(chǎng)有多種模式掸宛,由其modalPresentationStyle屬性決定招拙,有兩種模式可以進(jìn)行自定義: UIModalPresentationFullScreen 模式(默認(rèn)值)和 UIModalPresentationCustom 模式饰序。
Modal轉(zhuǎn)場(chǎng)和上面兩種轉(zhuǎn)場(chǎng)有點(diǎn)兒差別。上面兩種轉(zhuǎn)場(chǎng)中fromVC和toVC都是處在同一個(gè)容器中掠械,但是在model轉(zhuǎn)場(chǎng)中并沒有容器這玩意兒均唉。還有一個(gè)不同就是以上兩種轉(zhuǎn)場(chǎng)方式在轉(zhuǎn)場(chǎng)開始時(shí)候fromVC會(huì)被自動(dòng)加入containerView舔箭,結(jié)束后會(huì)自動(dòng)被移除。但是modal轉(zhuǎn)場(chǎng)如果是UIModalPresentationCustom模式下fromView在轉(zhuǎn)場(chǎng)結(jié)束后不會(huì)被移除箫章。
上圖是modal轉(zhuǎn)場(chǎng)的視圖層次區(qū)別。
從圖中可以看出
modal轉(zhuǎn)場(chǎng)中兩個(gè)視圖不處于同一層次,present的時(shí)候toView也就是presentedView要主動(dòng)加到containerView中镜会,但是dismiss的時(shí)候toView是presentingView檬寂,他不需要添加到containerView中。這個(gè)很重要戳表。
modal轉(zhuǎn)場(chǎng)的實(shí)現(xiàn)也和上面一個(gè)套路桶至,1.提供代理 2.提供動(dòng)畫控制器.
上代碼
動(dòng)畫控制器
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface EDModalAnimator : NSObject<UIViewControllerAnimatedTransitioning>
@end
#import "EDModalAnimator.h"
@implementation EDModalAnimator
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.3;
}
//實(shí)現(xiàn)了一個(gè)縮放動(dòng)畫,要注意在Custom模式下dismiss時(shí)候不要將toView添加到containerView上
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = transitionContext.containerView;
UIView *fromView = fromVC.view;
UIView *toView = toVC.view;
//present
if (toVC.isBeingPresented) {
[containerView addSubview:toView];
toView.center = containerView.center;
toView.bounds = CGRectMake(0, 0, containerView.frame.size.width*2/3, containerView.frame.size.height*2/3);
toView.transform = CGAffineTransformMakeScale(0.1, 0.1);
[UIView animateWithDuration:0.3 animations:^{
toView.transform = CGAffineTransformMakeScale(1, 1);
} completion:^(BOOL finished) {
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
//Modal 轉(zhuǎn)場(chǎng)在 Custom 模式下必須區(qū)分 presentation 和 dismissal 轉(zhuǎn)場(chǎng)(此時(shí) dismissal 轉(zhuǎn)場(chǎng)中不要將 toView 添加到 containerView)
if (fromVC.beingDismissed) {
if (transitionContext.presentationStyle != UIModalPresentationCustom) {
[containerView addSubview:toView];
}
[UIView animateWithDuration:0.3 animations:^{
fromView.transform = CGAffineTransformMakeScale(0.1, 0.1);
} completion:^(BOOL finished) {
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
}
@end
代理類
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface ModelTransitionDelegate : NSObject<UIViewControllerTransitioningDelegate>
@end
#import "ModelTransitionDelegate.h"
#import "EDPressentationController.h"
#import "EDModalAnimator.h"
@implementation ModelTransitionDelegate
//出現(xiàn)時(shí)的動(dòng)畫
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
return [EDModalAnimator new];
}
//關(guān)閉時(shí)的動(dòng)畫
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
return [EDModalAnimator new];
}
-(UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source
{
return [[EDPressentationController alloc]initWithPresentedViewController:presented presentingViewController:presenting];
}
Presenting類和Presented類
#import <UIKit/UIKit.h>
@interface PresentingVC : UIViewController
@end
#import "PresentingVC.h"
#import "ModelTransitionDelegate.h"
#import "PresentedVC.h"
@interface PresentingVC ()
@property(nonatomic,strong)ModelTransitionDelegate *transitionDelegate;
@end
@implementation PresentingVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithRed:255.0/255 green:83.0/255 blue:89.0/255 alpha:1];
UIButton *presentBtn = [[UIButton alloc]initWithFrame:CGRectMake(146.5, 313, 82, 41)];
[presentBtn setTitle:@"Present" forState:UIControlStateNormal];
[presentBtn addTarget:self action:@selector(goNext:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:presentBtn];
}
-(id<UIViewControllerTransitioningDelegate>)transitioningDelegate
{
if (!_transitionDelegate) {
_transitionDelegate = [ModelTransitionDelegate new];
}
return _transitionDelegate;
}
-(void)goNext:(UIButton *)sender
{
PresentedVC *nextVC = [PresentedVC new];
nextVC.transitioningDelegate = self.transitioningDelegate;
nextVC.modalPresentationStyle = UIModalPresentationCustom;
[self presentViewController:nextVC animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
#import <UIKit/UIKit.h>
@interface PresentedVC : UIViewController
@end
#import "PresentedVC.h"
@interface PresentedVC ()
@end
@implementation PresentedVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
UIButton *closeBtn = [[UIButton alloc]initWithFrame:CGRectMake(100, 200, 50, 30)];
[closeBtn setTitle:@"Close" forState:UIControlStateNormal];
[closeBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[closeBtn addTarget:self action:@selector(dismiss) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:closeBtn];
}
-(void)dismiss
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
代理類的最后有個(gè)EDPressentationController他是UIPresentationController的子類镣屹,iOS8之后添加的東西,前面多次在動(dòng)畫控制器中用到一個(gè)contanierView.打斷點(diǎn)可以看到他是一個(gè)UITansitionView价涝。其實(shí)他就是EDPressentationController的view女蜈。
ios8之后提供了UIPresentationController類,讓我們可以在contanierView上做些事情色瘩,利用轉(zhuǎn)場(chǎng)協(xié)調(diào)器還可以和動(dòng)畫控制器同步進(jìn)行動(dòng)畫鞭光,比如添加一個(gè)灰色半透明背景。他看起來是這樣的:
#import <UIKit/UIKit.h>
@interface EDPressentationController : UIPresentationController
@end
#import "EDPressentationController.h"
@interface EDPressentationController()
@property(nonatomic,strong)UIView *backView;
@end
@implementation EDPressentationController
-(UIView *)backView
{
if (!_backView) {
_backView = [[UIView alloc]init];
_backView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.2];
_backView.alpha = 0;
}
return _backView;
}
//Presentation 轉(zhuǎn)場(chǎng)開始前該方法被調(diào)用泞遗。
-(void)presentationTransitionWillBegin
{
[self.containerView addSubview:self.backView];
[self.presentedViewController.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
//這里的動(dòng)畫與專場(chǎng)動(dòng)畫同步執(zhí)行
self.backView.alpha = 1;
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
}];
}
//Dismissal 轉(zhuǎn)場(chǎng)開始前該方法被調(diào)用惰许。
-(void)dismissalTransitionWillBegin
{
[self.presentedViewController.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
self.backView.alpha = 0;
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
}];
}
//iOS 8 帶來了適應(yīng)性布局,<UIContentContainer>協(xié)議用于響應(yīng)視圖尺寸變化和屏幕旋轉(zhuǎn)事件史辙,之前用于處理屏幕旋轉(zhuǎn)的方法都被廢棄了汹买。UIViewController 和 UIPresentationController 類都遵守該協(xié)議佩伤。
//這個(gè)方法負(fù)責(zé)布局
-(void)containerViewWillLayoutSubviews
{
self.backView.center = self.containerView.center;
self.backView.bounds = self.containerView.bounds;
}
//轉(zhuǎn)場(chǎng)后presentingView是否被移除 默認(rèn)NO
-(BOOL)shouldRemovePresentersView
{
return NO;
}
@end
UIPresentationController
這個(gè)玩意兒有必要單獨(dú)說下,他是ios8以后提供的api晦毙。他的作用是用于展示一個(gè)controller,其實(shí)modal方式去展示一個(gè)controller就是用的這個(gè)玩意兒生巡,轉(zhuǎn)場(chǎng)時(shí)候可以通過定制這個(gè)類的子類,然后在轉(zhuǎn)場(chǎng)協(xié)議中提供他见妒,轉(zhuǎn)場(chǎng)的時(shí)候就會(huì)用這個(gè)自定義的孤荣,而不是系統(tǒng)默認(rèn)的。這樣就可以做到:
1.定制presentedView尺寸以及在containerView中添加自定會(huì)視圖并為這些視圖添加動(dòng)畫须揣。
2.可以選擇是否移除presentingView盐股。
3.可以在沒有動(dòng)畫控制器的情況下單獨(dú)工作,這個(gè)準(zhǔn)確說是當(dāng)我們不提供動(dòng)畫控制器的時(shí)候耻卡,他會(huì)用系統(tǒng)自帶的疯汁。
結(jié)束
到這里官方支持的定制轉(zhuǎn)場(chǎng)已經(jīng)結(jié)束,但是還有個(gè)問題沒有解決,就是如何為自定義的ViewController容器添加轉(zhuǎn)場(chǎng)卵酪。
這個(gè)自定義容器不繼承系統(tǒng)提供的容器(如UITabBarController),他直接繼承UIViewController幌蚊。這也是我研究控制器轉(zhuǎn)場(chǎng)的目的。希望能夠自己實(shí)現(xiàn)比較靠譜的controler容器溃卡,從而增加app的體驗(yàn)溢豆,使之區(qū)別于一般app的navigation加tabbar的這種app架構(gòu)。