效果圖
交互式動(dòng)畫(huà)的實(shí)現(xiàn)過(guò)程
動(dòng)畫(huà)
1悔常、給UINavigationController添加代理护赊,需實(shí)現(xiàn)UINavigationControllerDelegate
協(xié)議框仔。
2舀武、創(chuàng)建動(dòng)畫(huà)類(lèi),該類(lèi)需實(shí)現(xiàn)UIViewControllerAnimatedTransitioning
協(xié)議离斩。
手勢(shì)交互
3银舱、給UINavigationController添加手勢(shì)和完成手勢(shì)百分比的判定。
4跛梗、實(shí)現(xiàn)UIViewControllerAnimatedTransitioning
協(xié)議的手勢(shì)方法寻馏,返回UIPercentDrivenInteractiveTransition
對(duì)象。
給UINavigationController添加代理
創(chuàng)建PJNavigationInteractiveTransition
類(lèi)用于對(duì)UINavigationController代理的實(shí)現(xiàn)
// PJNavigationInteractiveTransition.h
self.navTransition =
[[PJNavigationInteractiveTransition alloc] initWithViewController:self];
// PJNavigationInteractiveTransition.h
- (instancetype)initWithViewController:(UINavigationController *)vc {
self = [super init];
if (self) {
_navigationController = vc;
_navigationController.delegate = self;
}
return self;
}
實(shí)現(xiàn)UINavigationControllerDelegate
協(xié)議
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC {
if (operation == UINavigationControllerOperationPop) {
return [[PJPopAnimation alloc] init];
}
_fromViewController = fromVC;
return nil;
}
- 當(dāng)調(diào)用
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
方法后系統(tǒng)將自動(dòng)調(diào)用該方法核偿。 - 當(dāng)返回為nil時(shí)诚欠,將使用默認(rèn)的轉(zhuǎn)場(chǎng)。否者則使用特定的動(dòng)畫(huà)類(lèi)漾岳。
實(shí)現(xiàn)動(dòng)畫(huà)類(lèi)
動(dòng)畫(huà)類(lèi)主要就是實(shí)現(xiàn)UINavigationControllerDelegate
協(xié)議中的兩個(gè)required方法
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 0.3;
}
這個(gè)方法就是返回動(dòng)畫(huà)執(zhí)行的時(shí)間
</br>
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
// 1
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// 2
UIView *fromView;
UIView *toView;
if ([transitionContext respondsToSelector:@selector(viewForKey:)]) {
fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
toView = [transitionContext viewForKey:UITransitionContextToViewKey];
} else {
fromView = fromViewController.view;
toView = toViewController.view;
}
// 3
fromView.frame = [transitionContext initialFrameForViewController:fromViewController];
toView.frame = [transitionContext finalFrameForViewController:toViewController];
// 4
UIView *containerView = [transitionContext containerView];
// 5
[containerView insertSubview:toView belowSubview:fromView];
// 6
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromView.transform = CGAffineTransformMakeTranslation([UIScreen mainScreen].bounds.size.width, 0);
} completion:^(BOOL finished) {
fromView.transform = CGAffineTransformIdentity;
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
1轰绵、獲取fromViewController與toViewController
fromViewController與toViewController是相對(duì)的,如下圖所示
2尼荆、獲取fromView與toView
當(dāng)系統(tǒng)版本為iOS8.0以上時(shí)左腔,需使用viewForKey。
3捅儒、設(shè)置frame液样。
4、獲取containerView
containerView就是轉(zhuǎn)場(chǎng)動(dòng)畫(huà)執(zhí)行中屏幕所展示的View巧还,相當(dāng)于是動(dòng)畫(huà)展示的平臺(tái)鞭莽。
5、將fromView與toView加入至containerView中
6麸祷、設(shè)置動(dòng)畫(huà)
當(dāng)動(dòng)畫(huà)結(jié)束后必須調(diào)用completeTransition方法以告知系統(tǒng)動(dòng)畫(huà)完成澎怒。
給UINavigationController添加手勢(shì)和完成手勢(shì)百分比的判定
給UINavigationController添加手勢(shì)
UIGestureRecognizer *gesture = self.interactivePopGestureRecognizer;
gesture.enabled = NO;
UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizer alloc] init];
popRecognizer.delegate = self;
popRecognizer.maximumNumberOfTouches = 1;
[gesture.view addGestureRecognizer:popRecognizer];
self.navTransition = [[PJNavigationInteractiveTransition alloc] initWithViewController:self];
[popRecognizer addTarget:self.navTransition action:@selector(handleControllerPop:)];
- 禁止系統(tǒng)的側(cè)滑返回
- 添加我們自己的Pan手勢(shì)
手勢(shì)的限制
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer {
// Ignore when no view controller is pushed into the navigation stack.
if (self.viewControllers.count <= 1) {
return NO;
}
// Ignore when the beginning location is beyond max allowed initial distance to left edge.
CGPoint beginningLocation = [gestureRecognizer locationInView:gestureRecognizer.view];
if (maxAllowedInitialDistance > 0 && beginningLocation.x > maxAllowedInitialDistance) {
return NO;
}
// Ignore pan gesture when the navigation controller is currently in transition.
if ([[self.navigationController valueForKey:@"_isTransitioning"] boolValue]) {
return NO;
}
// Prevent calling the handler when the gesture begins in an opposite direction.
CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view];
if (translation.x <= 0) {
return NO;
}
// When the active view controller allow interactive pop.
UIViewController *topViewController = self.viewControllers.lastObject;
if (topViewController.pj_interactivePopDisabled) {
return NO;
}
return YES;
}
手勢(shì)百分比的判定
- (void)handleControllerPop:(UIGestureRecognizer *)gestureRecognizer {
/**
* 穩(wěn)定進(jìn)度區(qū)間,讓它在0.0(未完成)~1.0(已完成)之間
*/
CGFloat progress = 0.0;
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
UIPanGestureRecognizer *recognizer = (UIPanGestureRecognizer *)gestureRecognizer;
progress = [recognizer translationInView:recognizer.view].x / recognizer.view.bounds.size.width;
progress = MIN(1.0, MAX(0.0, progress));
}
[self p_handleGestureRecognizer:gestureRecognizer Progress:progress];
}
- (void)p_handleGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer Progress:(CGFloat)progress {
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
self.interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
[self.navigationController popViewControllerAnimated:YES];
} else if (gestureRecognizer.state == UIGestureRecognizerStateChanged) {
[self.interactivePopTransition updateInteractiveTransition:progress];
} else if (gestureRecognizer.state == UIGestureRecognizerStateEnded ||
gestureRecognizer.state == UIGestureRecognizerStateCancelled) {
if (progress > 0.5) {
[self.interactivePopTransition finishInteractiveTransition];
if (_fromViewController && _fromViewController.pj_transitionFinishedBlock) {
_fromViewController.pj_transitionFinishedBlock();
}
} else {
[self.interactivePopTransition cancelInteractiveTransition];
}
self.interactivePopTransition = nil;
}
}
UIPercentDrivenInteractiveTransition是什么
這是一個(gè)實(shí)現(xiàn)了UIViewControllerInteractiveTransitioning接口的類(lèi)摇锋,為我們預(yù)先實(shí)現(xiàn)和提供了一系列便利的方法丹拯,可以用一個(gè)百分比來(lái)控制交互式切換的過(guò)程。具體有以下幾個(gè)重要方法:
-(void)updateInteractiveTransition:(CGFloat)percentComplete
更新百分比荸恕,一般通過(guò)手勢(shì)識(shí)別的長(zhǎng)度之類(lèi)的來(lái)計(jì)算一個(gè)值乖酬,然后進(jìn)行更新。
-(void)cancelInteractiveTransition
報(bào)告交互取消融求,返回切換前的狀態(tài)
–(void)finishInteractiveTransition
報(bào)告交互完成咬像,更新到切換后的狀態(tài)
實(shí)現(xiàn)UIViewControllerAnimatedTransitioning
協(xié)議的手勢(shì)方法
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
if ([animationController isKindOfClass:[PJPopAnimation class]]) {
return self.interactivePopTransition;
}
return nil;
}
參考
https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html#//apple_ref/doc/uid/TP40007457-CH16-SW1
https://developer.apple.com/library/content/samplecode/CustomTransitions/Introduction/Intro.html#//apple_ref/doc/uid/TP40015158-Intro-DontLinkElementID_2
https://onevcat.com/2013/10/vc-transition-in-ios7/
https://github.com/forkingdog/FDFullscreenPopGesture