以下是iOS7之后,蘋(píng)果開(kāi)放轉(zhuǎn)場(chǎng)動(dòng)畫(huà)接口結(jié)構(gòu)圖,分別是UITabBarControllerDelegate 、UIViewControllerTransitiningDelegate、UINavigationControllerDelegate
第一節(jié):ViewController的模態(tài)跳轉(zhuǎn):動(dòng)畫(huà)自定義
UIViewControllerTransitioningDelegate
這個(gè)函數(shù)用來(lái)設(shè)置調(diào)用present
方法和dismiss
方法時(shí),進(jìn)行的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
/// 執(zhí)行present方法,進(jìn)行的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
/// presented:將要彈出的Controller
/// presenting:當(dāng)前的Controller
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
/// 執(zhí)行dismiss方法,進(jìn)行的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
/// 執(zhí)行present方法,進(jìn)行的交互式轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator;
/// 執(zhí)行dismiss方法,進(jìn)行的交互式轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator;
/// iOS8后提供的新接口 返回UIPresentationController處理轉(zhuǎn)場(chǎng)
- (nullable UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(nullable UIViewController *)presenting sourceViewController:(UIViewController *)source API_AVAILABLE(ios(8.0));
UIViewControllerAnimatedTransitioning
接口,是自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的重點(diǎn)
@protocol UIViewControllerAnimatedTransitioning <NSObject>
/// 返回動(dòng)畫(huà)執(zhí)行的時(shí)長(zhǎng)
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
/// 自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的實(shí)現(xiàn)
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
@optional
UIViewControllerContextTransitioning
接口,轉(zhuǎn)場(chǎng)上下文,能獲取到轉(zhuǎn)場(chǎng)動(dòng)畫(huà)中參數(shù)及狀態(tài)
@protocol UIViewControllerContextTransitioning <NSObject>
/// 容器視圖: 用來(lái)表現(xiàn)動(dòng)畫(huà)
@property(nonatomic, readonly) UIView *containerView;
/// 是否應(yīng)該執(zhí)行動(dòng)畫(huà)
@property(nonatomic, readonly, getter=isAnimated) BOOL animated;
/// 是否可狡猾
@property(nonatomic, readonly, getter=isInteractive) BOOL interactive;
/// 是否被取消了
@property(nonatomic, readonly) BOOL transitionWasCancelled;
/// 轉(zhuǎn)場(chǎng)的風(fēng)格
@property(nonatomic, readonly) UIModalPresentationStyle presentationStyle;
/*
可交互轉(zhuǎn)場(chǎng)動(dòng)畫(huà)特有的
*/
/// 更新轉(zhuǎn)場(chǎng)過(guò)程的百分比,用于可交互動(dòng)畫(huà)的閥值
- (void)updateInteractiveTransition:(CGFloat)percentComplete;
/// 完成可交互的轉(zhuǎn)場(chǎng)交互動(dòng)作時(shí)調(diào)用
- (void)finishInteractiveTransition;
/// 取消可交互的轉(zhuǎn)場(chǎng)交互動(dòng)作時(shí)調(diào)用
- (void)cancelInteractiveTransition;
/// 轉(zhuǎn)場(chǎng)動(dòng)畫(huà)被中斷、暫停時(shí)調(diào)用
- (void)pauseInteractiveTransition API_AVAILABLE(ios(10.0));
/// 轉(zhuǎn)場(chǎng)動(dòng)畫(huà)完成時(shí)調(diào)用
- (void)completeTransition:(BOOL)didComplete;
/*
獲取轉(zhuǎn)場(chǎng)中兩個(gè)視圖控制器
UITransitionContextViewControllerKey 的定義
UITransitionContextFromViewControllerKey /// 原視圖控制器
UITransitionContextToViewControllerKey /// 跳轉(zhuǎn)的視圖控制器
*/
- (nullable __kindof UIViewController *)viewControllerForKey:(UITransitionContextViewControllerKey)key;
/*
直接獲取轉(zhuǎn)場(chǎng)中的視圖
UITransitionContextViewKey 的定義
UITransitionContextFromViewKey /// 原控制器的視圖
UITransitionContextToViewKey /// 轉(zhuǎn)場(chǎng)控制器的視圖
*/
- (nullable __kindof UIView *)viewForKey:(UITransitionContextViewKey)key API_AVAILABLE(ios(8.0));
@property(nonatomic, readonly) CGAffineTransform targetTransform API_AVAILABLE(ios(8.0));
/// 獲取視圖控制器的初始位置
- (CGRect)initialFrameForViewController:(UIViewController *)vc;
/// 獲取視圖控制器轉(zhuǎn)場(chǎng)后的位置
- (CGRect)finalFrameForViewController:(UIViewController *)vc;
@end
小案例:自定義一個(gè)從右邊滑入的present轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
第一步:
自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
,創(chuàng)建PanPresentAnimation實(shí)現(xiàn)UIViewControllerAnimatedTransitioning
接口
@interface PanPresentAnimation : NSObject<UIViewControllerAnimatedTransitioning>
@end
@implementation PanPresentAnimation
/// 設(shè)置轉(zhuǎn)場(chǎng)動(dòng)畫(huà)時(shí)間
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 0.8f;
}
/// 自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
/// 獲取切入ViewController
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
/// 設(shè)置要切入的ViewController的初始位置
/// 實(shí)現(xiàn)的效果:從右邊滑入的效果
CGRect screenBounds = [[UIScreen mainScreen] bounds];
CGRect finalFrame = [transitionContext finalFrameForViewController:toVC];
toVC.view.frame = CGRectOffset(finalFrame, screenBounds.size.width, 0);
/// 將切入的ViewController的View添加到containerView
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toVC.view];
/// 自動(dòng)定義動(dòng)畫(huà):彈出動(dòng)畫(huà)
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateWithDuration:duration
delay:0.0
usingSpringWithDamping:0.8
initialSpringVelocity:5
options:UIViewAnimationOptionCurveLinear
animations:^{
toVC.view.frame = finalFrame;
} completion:^(BOOL finished) {
/// 必須要告訴context 切換完成
[transitionContext completeTransition:YES];
}];
}
@end
第二步:
遵守UIViewControllerTransitioningDelegate協(xié)議
,返回自定義present轉(zhuǎn)場(chǎng)動(dòng)畫(huà)PanPresentAnimation
#import "AViewController.h"
#import "BViewController.h"
#import "PanPresentAnimation"
@interface MainViewController ()<UIViewControllerTransitioningDelegate>
@end
@implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
[button setTitle:@"Click me" forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
-(void) buttonClicked:(id)sender {
BViewController *mvc = [[BViewController alloc] init];
mvc.transitioningDelegate = self;
mvc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:mvc animated:YES completion:nil];
}
/// 返回自定義的present轉(zhuǎn)場(chǎng)動(dòng)畫(huà)對(duì)象:BouncePresentAnimation
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
return [PanPresentAnimation new];
}
@end
可交互轉(zhuǎn)場(chǎng)動(dòng)畫(huà)自定義
可交互轉(zhuǎn)場(chǎng)動(dòng)畫(huà),按我的理解就是可以控制轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的進(jìn)度
,比如蘋(píng)果系統(tǒng)提供的右滑返回上一級(jí)一樣。
在UIViewControllerTransitioningDelegate協(xié)議中有兩個(gè)可以定義可交互轉(zhuǎn)場(chǎng)的方法:interactionControllerForPresentation
方法很少用,因?yàn)橄乱患?jí)界面是不確定的,常用的是interactionControllerForDismissal
,因?yàn)樯弦患?jí)界面是確定的。
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator;
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator;
對(duì)于UIViewControllerTransitioningDelegate協(xié)議中對(duì)于dismiss轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的方法有以下兩個(gè):
一個(gè)是不可交互
的普通轉(zhuǎn)場(chǎng):animationControllerForDismissedController
一個(gè)是可交互
的轉(zhuǎn)場(chǎng):interactionControllerForDismissal
兩者的關(guān)系蘋(píng)果官方是這么寫(xiě)的:
(1)interactionControllerForDismissal方法中的
anmatore參數(shù)
是由animationControllerForDismissedController方法返回的
(2)如果interactionControllerForDismissal 返回結(jié)果是nil
,則是不執(zhí)行可交互動(dòng)畫(huà)
,執(zhí)行普通的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
(即animationControllerForDismissedController返回定義的動(dòng)畫(huà))
(3)如果要執(zhí)行可交互動(dòng)畫(huà)
,那么也必須要實(shí)現(xiàn)animationControllerForDismissedController
方法,并返回一個(gè)普通的轉(zhuǎn)場(chǎng)動(dòng)畫(huà),如果animationControllerForDismissedController返回是nil或沒(méi)實(shí)現(xiàn),那么interactionControllerForDismissal方法是不會(huì)執(zhí)行的
小案例:在上一個(gè)案例中繼續(xù)開(kāi)發(fā),在presentB界面后,自定義dismiss可交換的轉(zhuǎn)場(chǎng)動(dòng)畫(huà),效果:右滑返回。(案例:A present B,B dismiss A)
第一步:創(chuàng)建自定義可交換動(dòng)畫(huà),
PanInteractiveTransition
實(shí)現(xiàn)UIViewControllerInteractiveTransitioning
接口
@interface PanInteractiveTransition : NSObject <UIViewControllerInteractiveTransitioning>
/// 是否處于交互的狀態(tài)
@property (nonatomic, assign) BOOL interacting;
/// 用于為B界面添加拖動(dòng)手勢(shì)
/// @param viewController BController
- (void)forPresentingViewController:(UIViewController *)viewController;
@end
@interface PanInteractiveTransition()
@property (nonatomic, strong) id<UIViewControllerContextTransitioning> context;
@property (nonatomic, strong) UIViewController *presentingViewController;
@end
@implementation PanInteractiveTransition
/// 為B界面添加拖動(dòng)手勢(shì)
- (void)forPresentingViewController:(UIViewController *)viewController {
self.presentingViewController = viewController;
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handleGesgture:)];
[viewController.view addGestureRecognizer: pan];
}
/// 拖動(dòng)手勢(shì)響應(yīng)
- (void)handleGesgture:(UIPanGestureRecognizer *)pan {
CGPoint translation = [pan translationInView:pan.view.superview];
CGFloat persent = translation.x / [UIScreen mainScreen].bounds.size.width;
if (persent < 0) {
return;
}
persent = fabs(persent);
switch (pan.state) {
/// 手勢(shì)開(kāi)始
case UIGestureRecognizerStateBegan:
/// 標(biāo)記:交互中
self.interacting = YES;
/// 執(zhí)行dismiss方法
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
break;
/// 拖動(dòng)中
case UIGestureRecognizerStateChanged: {
/// 更新轉(zhuǎn)場(chǎng)動(dòng)畫(huà)進(jìn)度
[self updateAniProgess:persent];
break;
}
/// 拖動(dòng)結(jié)束
case UIGestureRecognizerStateEnded:
/// 拖動(dòng)取消、中斷
case UIGestureRecognizerStateCancelled: {
/// 標(biāo)記:交互結(jié)束
self.interacting = NO;
/// 如果滑動(dòng)查過(guò)50%,則返回,否則取消返回原點(diǎn)
if (persent > 0.5) {
[self finish];
}else{
[self cancel];
}
break;
}
default:
break;
}
}
/// UIViewControllerInteractiveTransitioning協(xié)議方法
/// 觸發(fā):開(kāi)始交互轉(zhuǎn)場(chǎng)時(shí)
/// 作用:將兩個(gè)ViewController中View的添加到視圖容器containerView中
- (void)startInteractiveTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
self.context = transitionContext;
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
toVC.view.frame = [transitionContext finalFrameForViewController:toVC];
[[transitionContext containerView] insertSubview:toVC.view belowSubview:fromVC.view];
}
/// 更新動(dòng)畫(huà)狀態(tài)
- (void)updateAniProgess:(CGFloat)progress {
UIView *frameVC = [self.context viewForKey:UITransitionContextFromViewKey];
CGRect finalRect = CGRectMake([UIScreen mainScreen].bounds.size.width * progress, 0, frameVC.bounds.size.width, frameVC.bounds.size.height);
frameVC.frame = finalRect;
}
/// 轉(zhuǎn)場(chǎng)結(jié)束
- (void)finish {
[UIView animateWithDuration:0.2 animations:^{
UIView *frameVC = [self.context viewForKey:UITransitionContextFromViewKey];
CGRect finalRect = CGRectMake([UIScreen mainScreen].bounds.size.width, 0, frameVC.bounds.size.width, frameVC.bounds.size.height);
frameVC.frame = finalRect;
} completion:^(BOOL finished) {
[self.context completeTransition:YES];
}];
}
/// 轉(zhuǎn)場(chǎng)取消
- (void)cancel {
[UIView animateWithDuration:0.2 animations:^{
UIView *frameVC = [self.context viewForKey:UITransitionContextFromViewKey];
CGRect finalRect = CGRectMake(0, 0, frameVC.bounds.size.width, frameVC.bounds.size.height);
frameVC.frame = finalRect;
} completion:^(BOOL finished) {
[self.context cancelInteractiveTransition];
}];
}
@end
第二步:實(shí)現(xiàn)
interactionControllerForDismissal
,返回自定義的可交互動(dòng)畫(huà)
#import "AController.h"
#import "BController.h"
#import "PanPresentAnimation.h"
#import "PanDimissAnimation.h"
#import "PanInteractiveTransition.h"
@interface AController ()<UIViewControllerTransitioningDelegate>
@property (nonatomic, strong) PanPresentAnimation *presentAnimation;
@property (nonatomic, strong) PanInteractiveTransition *panInteractiveTransition;
@end
@implementation AController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_presentAnimation = [PanPresentAnimation new];
_panInteractiveTransition = [PanInteractiveTransition new];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
[button setTitle:@"Click me" forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
-(void) buttonClicked:(id)sender {
BController *mvc = [[BController alloc] init];
mvc.transitioningDelegate = self;
mvc.modalPresentationStyle = UIModalPresentationFullScreen;
[self.panInteractiveTransition forPresentingViewController:mvc];
[self presentViewController:mvc animated:YES completion:nil];
}
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
/// 普通的present轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
/// 這里個(gè)類實(shí)現(xiàn)則寫(xiě)了,與present動(dòng)畫(huà)方式是一樣的,詳情請(qǐng)看最后的demo代碼
return self.presentAnimation;
}
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
/// 普通的dimiss轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
return [PanDimissAnimation new];
}
-(id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator {
/// 這里要判斷是否交互觸發(fā)的dismiss方法,如果不是,則返回nil,表示執(zhí)行普通dimiss轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
return self.panInteractiveTransition.interacting ? self.panInteractiveTransition : nil;
}
@end
補(bǔ)充:
可交互轉(zhuǎn)場(chǎng)接口UIViewControllerInteractiveTransitioning,系統(tǒng)還提供了”繼承于“ UIViewControllerInteractiveTransitioning的接口UIPercentDrivenInteractiveTransition
。
UIPercentDrivenInteractiveTransition
做的事跟我們上面自定義的PanInteractiveTransition
差不多,主要包括updateInteractiveTransition:
、cancelInteractiveTransition
、finishInteractiveTransition
,有些屬性是iOS10之后才引入的,為低版本兼容,就不采用。比如記錄是否處于可交互狀態(tài)wantsInteractiveStart
,而是使用自定義的屬性。
但是,但是,但是
,UIPercentDrivenInteractiveTransition
與UIViewControllerInteractiveTransitioning
還是很不一樣
的,官方文檔是這樣描述的:
大體的意思是:
(1)
UIPercentDrivenInteractiveTransition
對(duì)象依賴于UIViewControllerTransitioningDelegate
協(xié)議返回的動(dòng)畫(huà)對(duì)象去執(zhí)行設(shè)置動(dòng)畫(huà)和執(zhí)行動(dòng)畫(huà)的
(也就是說(shuō):UIViewControllerTransitioningDelegate
百分比可交互轉(zhuǎn)場(chǎng)動(dòng)畫(huà)UIPercentDrivenInteractiveTransition設(shè)置,是在協(xié)議方法中animationControllerForPresentedController:
和 animationControllerForDismissedController:
返回對(duì)象中設(shè)置的,還有一個(gè)要注意的地方,請(qǐng)看備注1
)(2)可交互轉(zhuǎn)場(chǎng)進(jìn)度,用戶可以使用
updateInteractiveTransition:
、finishInteractiveTransition
、cancelInteractiveTransition
三個(gè)方法控制
備注1:
以animationControllerForDismissedController:
方法為例,執(zhí)行completeTransition:
方法時(shí)就不能寫(xiě)死YES,需要設(shè)置成[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
因?yàn)榻换マD(zhuǎn)場(chǎng)可能取消了,那么就需要返回初始位置
案例:重寫(xiě)上例dismiss可交互返回的動(dòng)畫(huà)為例
@interface PanDimissAnimation : NSObject<UIViewControllerAnimatedTransitioning>
@end
@implementation PanDimissAnimation
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return 0.4f;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
NSLog(@"animateTransition: ===== %d",transitionContext.isInteractive);
/// 原VC
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
/// 跳轉(zhuǎn)VC
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
CGFloat screenW = [UIScreen mainScreen].bounds.size.width;
/// 獲取初始位置
CGRect initialFrame = [transitionContext initialFrameForViewController:fromVC];
/// 計(jì)算轉(zhuǎn)場(chǎng)最后的位置
CGRect finalFrame = CGRectOffset(initialFrame, screenW, 0);
/// 都需要將toVC.view添加到視圖容器containerView中,不論dismiss動(dòng)畫(huà)還是present動(dòng)畫(huà)
/// 將toVC.view 添加到視圖容器containerView中
/// 如果不添加也可以,不過(guò)效果不好,在切換的時(shí)候,會(huì)有一小段是白屏
/// 推薦添加,并且要置于底部,否則會(huì)覆蓋在fromVC.view 上面
[transitionContext.containerView addSubview:toVC.view];
[transitionContext.containerView sendSubviewToBack:toVC.view];
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
/// 設(shè)置最后的位置
fromVC.view.frame = finalFrame;
} completion:^(BOOL finished) {
/// 動(dòng)畫(huà)完成
/// (1)completeTransition調(diào)用如果返回YES,則會(huì)將fromVC.view 從ContainView中移除
/// 而這里是否完成通過(guò)![transitionContext transitionWasCancelled]賦值,
/// 因?yàn)閷?duì)于可交互動(dòng)畫(huà),是有可能被取消的,那么就需要把fromVC.view復(fù)原原來(lái)的位置,不能移除
/// (當(dāng)可交互動(dòng)畫(huà)是繼承于UIPercentDrivenInteractiveTransition就會(huì)出現(xiàn)這種情況)
/// (2)如果是沒(méi)有可交互的動(dòng)畫(huà),那么直接返回YES也是可以,但是推薦使用第一種方式
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
@end
@interface PanPercentIinteractiveTransition : UIPercentDrivenInteractiveTransition
/// 是否處于交互的狀態(tài)
@property (nonatomic, assign) BOOL interacting;
/// 用于為B界面添加拖動(dòng)手勢(shì)
/// @param viewController BController
- (void)forPresentingViewController:(UIViewController *)viewController;
@end
@interface PanPercentIinteractiveTransition()
@property (nonatomic, strong) UIViewController *presentingViewController;
@end
@implementation PanPercentIinteractiveTransition
/// 用于為B界面添加拖動(dòng)手勢(shì)
/// @param viewController BController
- (void)forPresentingViewController:(UIViewController *)viewController {
self.presentingViewController = viewController;
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handleGesgture:)];
[viewController.view addGestureRecognizer: pan];
}
/// 拖動(dòng)手勢(shì)響應(yīng)
- (void)handleGesgture:(UIPanGestureRecognizer *)pan {
CGPoint translation = [pan translationInView:pan.view.superview];
CGFloat persent = translation.x / [UIScreen mainScreen].bounds.size.width;
if (persent < 0) {
return;
}
persent = fabs(persent);
NSLog(@"========== persent:%f",persent);
switch (pan.state) {
/// 手勢(shì)開(kāi)始
case UIGestureRecognizerStateBegan: {
/// 標(biāo)記:交互中
self.interacting = YES;
/// 執(zhí)行dismiss方法
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
break;
/// 拖動(dòng)中
case UIGestureRecognizerStateChanged: {
NSLog(@"update=====");
/// 更新轉(zhuǎn)場(chǎng)動(dòng)畫(huà)進(jìn)度
[self updateInteractiveTransition:persent];
}
break;
/// 拖動(dòng)結(jié)束
case UIGestureRecognizerStateEnded:
/// 拖動(dòng)取消、中斷
case UIGestureRecognizerStateCancelled: {
/// 標(biāo)記:交互結(jié)束
self.interacting = NO;
/// 如果滑動(dòng)查過(guò)50%,則返回,否則取消返回原點(diǎn)
if (persent > 0.5) {
[self finishInteractiveTransition];
}else{
[self cancelInteractiveTransition];
}
}
break;
default:
break;
}
}
@end
DEMO
Present/Dimiss 代碼
鏈接: https://pan.baidu.com/s/1hkR_mhB2AsOq9hu82n-nOQ 密碼: 612k
第二節(jié):導(dǎo)航欄轉(zhuǎn)場(chǎng)動(dòng)畫(huà)自定義
由第一節(jié)已經(jīng)知道自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的流程及設(shè)計(jì)的類、接口等,其實(shí)導(dǎo)航欄轉(zhuǎn)場(chǎng)與模態(tài)轉(zhuǎn)場(chǎng)自定義也是一樣的基本原理,不同的是變成設(shè)置UINavigationController實(shí)例的delegate(
UINavigationControllerDelegate
),其余的都是一樣,這里就不展開(kāi)細(xì)講???♂?。
/// 設(shè)置轉(zhuǎn)場(chǎng)動(dòng)畫(huà),如果返回nil,則使用系統(tǒng)默認(rèn)的導(dǎo)航欄轉(zhuǎn)場(chǎng)動(dòng)畫(huà),不論P(yáng)USH或POP
/// UINavigationControllerOperation 枚舉
/// UINavigationControllerOperationNone, //無(wú)
/// UINavigationControllerOperationPush, //push操作
/// UINavigationControllerOperationPop, //pop操作
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC API_AVAILABLE(ios(7.0));
/// 設(shè)置可交互動(dòng)畫(huà)
- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController API_AVAILABLE(ios(7.0));
第三節(jié):tabBarController切換轉(zhuǎn)場(chǎng)動(dòng)畫(huà)自定義
UITabBarController也是自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的,套路一樣,只是delegate不同(
UITabBarControllerDelegate
)
/// 普通轉(zhuǎn)場(chǎng)
- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
animationControllerForTransitionFromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC API_AVAILABLE(ios(7.0));
/// 可交互轉(zhuǎn)場(chǎng)
- (nullable id <UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController
interactionControllerForAnimationController: (id <UIViewControllerAnimatedTransitioning>)animationController API_AVAILABLE(ios(7.0));
解惑:ContainerView
相信你在看文章的時(shí)候,一定有個(gè)疑惑,到底containerView
是什么鬼?是臨時(shí)的視圖容器么?它跟FromVC.view 與 ToVC.view 之間的視圖層級(jí)關(guān)系
到底是怎么樣?(這很重要,很關(guān)鍵,對(duì)于自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)有很大的幫助
)
筆者剛開(kāi)始的時(shí)候也是有很大的疑惑,接下來(lái)以 A PUSH B
為例,Demo代碼下載地
鏈接: https://pan.baidu.com/s/1Usk2KtjoLu2PenGusVZ3ig 密碼: tceb
從上圖可以看到containView是一直存在的(UINavigationController、UITabBarController、UIWindow都是帶有一個(gè)轉(zhuǎn)場(chǎng)視圖UITransitionView,用于展示轉(zhuǎn)場(chǎng)動(dòng)畫(huà)及控制器視圖的
),所以上文自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)中提到一定要將toVIewController.view 添加到ContainerView中。
參考文章
WWDC 2013 Session筆記 - iOS7中的ViewController切換
iOS自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
【實(shí)戰(zhàn)】快速集成自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)&手勢(shì)驅(qū)動(dòng)
iOS 自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)淺談
iOS自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)(push、pop動(dòng)畫(huà))
iOS 兩行代碼實(shí)現(xiàn)自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
使用 UIPercentDrivenInteractiveTransition