下一篇相關(guān)文章:UINavigationBar手勢(shì)側(cè)滑纱注、隱藏bar、UIScrollView側(cè)滑返回研究二
前言
iOS開(kāi)發(fā)中都會(huì)遇到的一個(gè)問(wèn)題胆胰,就是在iOS7以后蘋果自動(dòng)添加了側(cè)滑pop返回手勢(shì)狞贱,但一般APP由于設(shè)計(jì)的需要,會(huì)修改蘋果的backBarButtonItem或是leftBarButtonItem蜀涨,但只要修改這個(gè)就會(huì)導(dǎo)致蘋果定義的這個(gè)側(cè)滑pop返回的手勢(shì)失效瞎嬉,于是就有了以下多種八仙過(guò)海各顯神通的解決方式
-
方案一
作者:J_雨
demo:TestPopGestureSolution1
介紹文章: iOS利用Runtime自定義控制器POP手勢(shì)動(dòng)畫
- 方法一:
這篇文章介紹了2種方法,方法一是利用蘋果推薦的做法厚柳,就是重寫UINavigationController的兩個(gè)代理方法氧枣,navigationController:animationControllerForOperation:fromViewController:toViewController:
(返回一個(gè)處理交互的類)
navigationController:interactionControllerForAnimationController:
同時(shí)獲取UINavigationController的interactivePopGestureRecognizer
,并獲取這個(gè)pop手勢(shì)的View别垮,然后給這個(gè)View添加新的手勢(shì)處理
UIGestureRecognizer *gesture = self.interactivePopGestureRecognizer;
gesture.enabled = NO;
UIView *gestureView = gesture.view;
UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizer alloc] init];
popRecognizer.delegate = self;
popRecognizer.maximumNumberOfTouches = 1;
[gestureView addGestureRecognizer:popRecognizer];
_navT = [[NavigationInteractiveTransition alloc] initWithViewController:self];
[popRecognizer addTarget:_navT action:@selector(handleControllerPop:)];
具體手勢(shì)的處理以及UINavigationController的代理都在NavigationInteractiveTransition
中
#import "NavigationInteractiveTransition.h"
#import "PopAnimation.h"
@interface NavigationInteractiveTransition ()
@property (nonatomic, weak) UINavigationController *vc;
@property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactivePopTransition;
@end
@implementation NavigationInteractiveTransition
- (instancetype)initWithViewController:(UIViewController *)vc
{
self = [super init];
if (self) {
self.vc = (UINavigationController *)vc;
self.vc.delegate = self;
}
return self;
}
/**
* 我們把用戶的每次Pan手勢(shì)操作作為一次pop動(dòng)畫的執(zhí)行
*/
- (void)handleControllerPop:(UIPanGestureRecognizer *)recognizer {
/**
* interactivePopTransition就是我們說(shuō)的方法2返回的對(duì)象便监,我們需要更新它的進(jìn)度來(lái)控制Pop動(dòng)畫的流程,我們用手指在視圖中的位置與視圖寬度比例作為它的進(jìn)度碳想。
*/
CGFloat progress = [recognizer translationInView:recognizer.view].x / recognizer.view.bounds.size.width;
/**
* 穩(wěn)定進(jìn)度區(qū)間烧董,讓它在0.0(未完成)~1.0(已完成)之間
*/
progress = MIN(1.0, MAX(0.0, progress));
if (recognizer.state == UIGestureRecognizerStateBegan) {
/**
* 手勢(shì)開(kāi)始,新建一個(gè)監(jiān)控對(duì)象
*/
self.interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
/**
* 告訴控制器開(kāi)始執(zhí)行pop的動(dòng)畫
*/
[self.vc popViewControllerAnimated:YES];
}
else if (recognizer.state == UIGestureRecognizerStateChanged) {
/**
* 更新手勢(shì)的完成進(jìn)度
*/
[self.interactivePopTransition updateInteractiveTransition:progress];
}
else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled) {
/**
* 手勢(shì)結(jié)束時(shí)如果進(jìn)度大于一半胧奔,那么就完成pop操作逊移,否則重新來(lái)過(guò)。
*/
if (progress > 0.5) {
[self.interactivePopTransition finishInteractiveTransition];
}
else {
[self.interactivePopTransition cancelInteractiveTransition];
}
self.interactivePopTransition = nil;
}
}
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC {
/**
* 方法1中判斷如果當(dāng)前執(zhí)行的是Pop操作龙填,就返回我們自定義的Pop動(dòng)畫對(duì)象胳泉。
*/
if (operation == UINavigationControllerOperationPop)
return [[PopAnimation alloc] init];
return nil;
}
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
/**
* 方法2會(huì)傳給你當(dāng)前的動(dòng)畫對(duì)象animationController啡浊,判斷如果是我們自定義的Pop動(dòng)畫對(duì)象,那么就返回interactivePopTransition來(lái)監(jiān)控動(dòng)畫完成度胶背。
*/
if ([animationController isKindOfClass:[PopAnimation class]])
return self.interactivePopTransition;
return nil;
}
@end
具體的動(dòng)畫在PopAnimation
,改類實(shí)現(xiàn)UIViewControllerAnimatedTransitioning
這個(gè)轉(zhuǎn)場(chǎng)動(dòng)畫協(xié)議
具體實(shí)現(xiàn)
#import "PopAnimation.h"
@interface PopAnimation ()
@property (nonatomic, strong) id <UIViewControllerContextTransitioning> transitionContext;
@end
@implementation PopAnimation
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
//這個(gè)方法返回動(dòng)畫執(zhí)行的時(shí)間
return 0.25;
}
/**
* transitionContext你可以看作是一個(gè)工具巷嚣,用來(lái)獲取一系列動(dòng)畫執(zhí)行相關(guān)的對(duì)象,并且通知系統(tǒng)動(dòng)畫是否完成等功能钳吟。
*/
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
/**
* 獲取動(dòng)畫來(lái)自的那個(gè)控制器
*/
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
/**
* 獲取轉(zhuǎn)場(chǎng)到的那個(gè)控制器
*/
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
/**
* 轉(zhuǎn)場(chǎng)動(dòng)畫是兩個(gè)控制器視圖時(shí)間的動(dòng)畫廷粒,需要一個(gè)containerView來(lái)作為一個(gè)“舞臺(tái)”,讓動(dòng)畫執(zhí)行红且。
*/
UIView *containerView = [transitionContext containerView];
[containerView insertSubview:toViewController.view belowSubview:fromViewController.view];
NSTimeInterval duration = [self transitionDuration:transitionContext];
/**
* 執(zhí)行動(dòng)畫坝茎,我們讓fromVC的視圖移動(dòng)到屏幕最右側(cè)
*/
[UIView animateWithDuration:duration animations:^{
fromViewController.view.transform = CGAffineTransformMakeTranslation([UIScreen mainScreen].bounds.size.width, 0);
}completion:^(BOOL finished) {
/**
* 當(dāng)你的動(dòng)畫執(zhí)行完成,這個(gè)方法必須要調(diào)用暇番,否則系統(tǒng)會(huì)認(rèn)為你的其余任何操作都在動(dòng)畫執(zhí)行過(guò)程中嗤放。
*/
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
// _transitionContext = transitionContext;
//----------------pop動(dòng)畫一-------------------------//
/*
[UIView beginAnimations:@"View Flip" context:nil];
[UIView setAnimationDuration:duration];
[UIView setAnimationDelegate:self];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:containerView cache:YES];
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:)];
[UIView commitAnimations];//提交UIView動(dòng)畫
[containerView exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
*/
//----------------pop動(dòng)畫二-------------------------//
/*
CATransition *tr = [CATransition animation];
tr.type = @"cube";
tr.subtype = @"fromLeft";
tr.duration = duration;
tr.removedOnCompletion = NO;
tr.fillMode = kCAFillModeForwards;
tr.delegate = self;
[containerView.layer addAnimation:tr forKey:nil];
[containerView exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
*/
}
- (void)animationDidStop:(CATransition *)anim finished:(BOOL)flag {
[_transitionContext completeTransition:!_transitionContext.transitionWasCancelled];
}
@end
- 方法二
使用Runtime+KVC的方式,給UINavigationController的interactivePopGestureRecognizer.view添加一個(gè)自定義的手勢(shì)壁酬,然后處理這個(gè)手勢(shì)的target-action使用系統(tǒng)本身處理的target-action
UIGestureRecognizer *gesture = self.interactivePopGestureRecognizer;
gesture.enabled = NO;
UIView *gestureView = gesture.view;
UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizer alloc] init];
popRecognizer.delegate = self;
popRecognizer.maximumNumberOfTouches = 1;
[gestureView addGestureRecognizer:popRecognizer];
/**
* 獲取系統(tǒng)手勢(shì)的target數(shù)組
*/
NSMutableArray *_targets = [gesture valueForKey:@"_targets"];
/**
* 獲取它的唯一對(duì)象次酌,我們知道它是一個(gè)叫UIGestureRecognizerTarget的私有類,它有一個(gè)屬性叫_target
*/
id gestureRecognizerTarget = [_targets firstObject];
/**
* 獲取_target:_UINavigationInteractiveTransition舆乔,它有一個(gè)方法叫handleNavigationTransition:
*/
id navigationInteractiveTransition = [gestureRecognizerTarget valueForKey:@"_target"];
/**
* 通過(guò)前面的打印岳服,我們從控制臺(tái)獲取出來(lái)它的方法簽名。
*/
SEL handleTransition = NSSelectorFromString(@"handleNavigationTransition:");
/**
* 創(chuàng)建一個(gè)與系統(tǒng)一模一樣的手勢(shì)希俩,我們只把它的類改為UIPanGestureRecognizer
*/
[popRecognizer addTarget:navigationInteractiveTransition action:handleTransition];
以上兩種方式在使用時(shí)都需要處理已經(jīng)是pop到rootViewController后的情況吊宋,以及要處理正在側(cè)滑動(dòng)畫時(shí)不能繼續(xù)走這個(gè)側(cè)滑轉(zhuǎn)場(chǎng)動(dòng)畫,所以作者添加了對(duì)gesture代理的判斷
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
/**
* 這里有兩個(gè)條件不允許手勢(shì)執(zhí)行颜武,1璃搜、當(dāng)前控制器為根控制器;2鳞上、如果這個(gè)push这吻、pop動(dòng)畫正在執(zhí)行(私有屬性)
*/
return self.viewControllers.count != 1 && ![[self valueForKey:@"_isTransitioning"] boolValue];
}
作者demo鏈接:CustomPopAnimation
-
方案二
作者:forkingdog
demo:TestPopGestureSolution2
文章說(shuō)明:一個(gè)絲滑的全屏滑動(dòng)返回手勢(shì)
GitHub地址:FDFullscreenPopGesture
這個(gè)庫(kù)作者說(shuō)是參考了上面列的J_雨的那個(gè)Runtime+KVC的天才思路,具體的實(shí)現(xiàn)做了更多的封裝與優(yōu)化因块,主要使用分類category的方法無(wú)侵入的給所有UINavigationController添加側(cè)滑返回的手勢(shì)橘原,這樣方便無(wú)論使用了自動(dòng)有nav還是系統(tǒng)nav都能不做任何修改就能集成到項(xiàng)目中
具體的代碼
分類UINavigationController(FDFullscreenPopGesture)
處理側(cè)滑返回
分類UIViewController (FDFullscreenPopGesture)
添加幾個(gè)屬性,讓UIViewController可以方便調(diào)用一些基本的操作
fd_interactivePopDisabled
是否禁用側(cè)滑返回手勢(shì)
fd_prefersNavigationBarHidden
隱藏或顯示導(dǎo)航欄
fd_interactivePopMaxAllowedInitialDistanceToLeftEdge
左側(cè)最大響應(yīng)側(cè)滑的位置
具體實(shí)現(xiàn):在load方法里使用Runtime修改UINavigationController的pushViewController:animated:
方法涡上,用fd_pushViewController:animated:
實(shí)現(xiàn)push趾断,如果當(dāng)前NavigationController沒(méi)有添加自定義的手勢(shì),則添加手勢(shì),同時(shí)給viewController添加viewWillAppear:
吩愧、viewWillAppear:
添加執(zhí)行block
- (void)fd_pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if (![self.interactivePopGestureRecognizer.view.gestureRecognizers containsObject:self.fd_fullscreenPopGestureRecognizer]) {
// Add our own gesture recognizer to where the onboard screen edge pan gesture recognizer is attached to.
[self.interactivePopGestureRecognizer.view addGestureRecognizer:self.fd_fullscreenPopGestureRecognizer];
// Forward the gesture events to the private handler of the onboard gesture recognizer.
NSArray *internalTargets = [self.interactivePopGestureRecognizer valueForKey:@"targets"];
id internalTarget = [internalTargets.firstObject valueForKey:@"target"];
SEL internalAction = NSSelectorFromString(@"handleNavigationTransition:");
self.fd_fullscreenPopGestureRecognizer.delegate = self.fd_popGestureRecognizerDelegate;
[self.fd_fullscreenPopGestureRecognizer addTarget:internalTarget action:internalAction];
// Disable the onboard gesture recognizer.
self.interactivePopGestureRecognizer.enabled = NO;
}
// Handle perferred navigation bar appearance.
[self fd_setupViewControllerBasedNavigationBarAppearanceIfNeeded:viewController];
// Forward to primary implementation.
if (![self.viewControllers containsObject:viewController]) {
[self fd_pushViewController:viewController animated:animated];
}
}
/**
給即將要顯示的Controller添加執(zhí)行viewWillAppear的block和viewWillDisappear的block
@param appearingViewController 即將出現(xiàn)的Controller
*/
- (void)fd_setupViewControllerBasedNavigationBarAppearanceIfNeeded:(UIViewController *)appearingViewController
{
if (!self.fd_viewControllerBasedNavigationBarAppearanceEnabled) {
return;
}
__weak typeof(self) weakSelf = self;
_FDViewControllerWillAppearInjectBlock block = ^(UIViewController *viewController, BOOL animated) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf setNavigationBarHidden:viewController.fd_prefersNavigationBarHidden animated:animated];
}
};
// Setup will appear inject block to appearing view controller.
// Setup disappearing view controller as well, because not every view controller is added into
// stack by pushing, maybe by "-setViewControllers:".
appearingViewController.fd_willAppearInjectBlock = block;
UIViewController *disappearingViewController = self.viewControllers.lastObject;
if (disappearingViewController && !disappearingViewController.fd_willAppearInjectBlock) {
disappearingViewController.fd_willAppearInjectBlock = block;
}
}
/**
自定義的處理全局側(cè)滑返回的gesture芋酌,使用runtime的方式保存變量
@return 處理返回的gesture
*/
- (UIPanGestureRecognizer *)fd_fullscreenPopGestureRecognizer
{
UIPanGestureRecognizer *panGestureRecognizer = objc_getAssociatedObject(self, _cmd);
if (!panGestureRecognizer) {
panGestureRecognizer = [[UIPanGestureRecognizer alloc] init];
panGestureRecognizer.maximumNumberOfTouches = 1;
objc_setAssociatedObject(self, _cmd, panGestureRecognizer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return panGestureRecognizer;
}
/**
處理側(cè)滑的gesture的代理
@return 處理側(cè)滑手勢(shì)的代理
*/
- (_FDFullscreenPopGestureRecognizerDelegate *)fd_popGestureRecognizerDelegate
{
_FDFullscreenPopGestureRecognizerDelegate *delegate = objc_getAssociatedObject(self, _cmd);
if (!delegate) {
delegate = [[_FDFullscreenPopGestureRecognizerDelegate alloc] init];
delegate.navigationController = self;
objc_setAssociatedObject(self, _cmd, delegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return delegate;
}
- (BOOL)fd_viewControllerBasedNavigationBarAppearanceEnabled
{
NSNumber *number = objc_getAssociatedObject(self, _cmd);
if (number) {
return number.boolValue;
}
self.fd_viewControllerBasedNavigationBarAppearanceEnabled = YES;
return YES;
}
- (void)setFd_viewControllerBasedNavigationBarAppearanceEnabled:(BOOL)enabled
{
SEL key = @selector(fd_viewControllerBasedNavigationBarAppearanceEnabled);
objc_setAssociatedObject(self, key, @(enabled), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
同時(shí)為了適配ViewController里本身有UIScrollView可以左右滾動(dòng)的情形,避免滾動(dòng)scrollView的手勢(shì)與側(cè)滑返回的手勢(shì)沖突雁佳,可以在UIScrollView里重寫方法判斷
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if (self.contentOffset.x <= 0) {
if ([otherGestureRecognizer.delegate isKindOfClass:NSClassFromString(@"_FDFullscreenPopGestureRecognizerDelegate")]) {
return YES;
}
}
return NO;
}
- 方案三
作者:譚真
demo:TestPopGestureSolution3
文章介紹:一行代碼脐帝,讓你的應(yīng)用中UIScrollView的滑動(dòng)與側(cè)滑返回并存
GitHub代碼:TZScrollViewPopGesture
說(shuō)明:
添加側(cè)滑只要設(shè)置UINavigationController的代理的navigationController: didShowViewController: animated:
方法里設(shè)置
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
navigationController.interactivePopGestureRecognizer.delegate = nil;
}
為了設(shè)置讓包含有scrollView的Controller也支持側(cè)滑pop返回同云,直接給scrollView添加一個(gè)pan手勢(shì),處理這個(gè)手勢(shì)的target-action是navgiationController.interactivePopGestureRecognizer.delegate的handleNavigationTransition方法
@implementation UIViewController (TZPopGesture)
- (void)tz_addPopGestureToView:(UIView *)view {
if (!view) return;
if (!self.navigationController) {
// 在控制器轉(zhuǎn)場(chǎng)的時(shí)候堵腹,self.navigationController可能是nil,這里用GCD和遞歸來(lái)處理這種情況
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self tz_addPopGestureToView:view];
});
} else {
UIPanGestureRecognizer *pan = self.tz_popGestureRecognizer;
if (![view.gestureRecognizers containsObject:pan]) {
[view addGestureRecognizer:pan];
}
}
}
- (UIPanGestureRecognizer *)tz_popGestureRecognizer {
UIPanGestureRecognizer *pan = objc_getAssociatedObject(self, _cmd);
if (!pan) {
// 側(cè)滑返回手勢(shì) 手勢(shì)觸發(fā)的時(shí)候炸站,讓target執(zhí)行action
id target = self.navigationController.tz_popDelegate;
SEL action = NSSelectorFromString(@"handleNavigationTransition:");
pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:action];
pan.maximumNumberOfTouches = 1;
// 這讓nav來(lái)處理是否響應(yīng)側(cè)滑
pan.delegate = self.navigationController;
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
objc_setAssociatedObject(self, _cmd, pan, OBJC_ASSOCIATION_ASSIGN);
}
return pan;
}
同時(shí) pan.delegate = self.navigationController;
對(duì)gesture攔截處理,讓nav可以對(duì)scrollView判斷是響應(yīng)側(cè)滑還是正常的滑動(dòng)
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer {
if ([[self valueForKey:@"_isTransitioning"] boolValue]) {
return NO;
}
if ([self.navigationController.transitionCoordinator isAnimated]) {
return NO;
}
if (self.childViewControllers.count <= 1) {
return NO;
}
UIViewController *vc = self.topViewController;
if (vc.tz_interactivePopDisabled) {
return NO;
}
// 側(cè)滑手勢(shì)觸發(fā)位置
CGPoint location = [gestureRecognizer locationInView:self.view];
CGPoint offSet = [gestureRecognizer translationInView:gestureRecognizer.view];
BOOL ret = (0 < offSet.x && location.x <= 40);
// NSLog(@"%@ %@",NSStringFromCGPoint(location),NSStringFromCGPoint(offSet));
return ret;
}
/// 只有當(dāng)系統(tǒng)側(cè)滑手勢(shì)失敗了,才去觸發(fā)ScrollView的滑動(dòng)
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
- 方案四
在實(shí)際開(kāi)發(fā)中使用過(guò)的一種方式一種簡(jiǎn)便的方式疚顷,在ViewController的viewDidLoad
里添加一行代碼(最好在base類里加或?qū)懸粋€(gè)分類統(tǒng)一加),這種方法簡(jiǎn)單高效但會(huì)面臨一些其他問(wèn)題旱易,參考文章:navigationController側(cè)滑
demo:TestPopGestureSolution4
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;
}
其他非主流方案
- 方案五
作者:參考網(wǎng)上想法
demo:TestPopGestureSolution5
實(shí)現(xiàn)方式:在UINavigationController里維護(hù)一個(gè)截圖的數(shù)組,同時(shí)讓原生的側(cè)滑返回失效
- (void)loadView{
[super loadView];
[self initilization];
}
- (void)initilization{
self.backImgs = [[NSMutableArray alloc] init];
}
- (void)loadBaseUI{
//原生方法無(wú)效
self.interactivePopGestureRecognizer.enabled = NO;
//設(shè)置手勢(shì)
self.panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureRecognizerAction:)];
[self.view addGestureRecognizer:self.panGestureRecognizer];
}
攔截push方法腿堤,讓push時(shí)設(shè)置一張截圖讓到數(shù)組里
#pragma mark- public method
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
//截圖
UIGraphicsBeginImageContextWithOptions([UIScreen mainScreen].bounds.size, YES, 1.0);
[[UIApplication sharedApplication].keyWindow.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.backImgs addObject:img];
[super pushViewController:viewController animated:animated];
}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated{
[_backImgs removeLastObject];
return [super popViewControllerAnimated:animated];
}
在側(cè)滑時(shí)阀坏,對(duì)手勢(shì)的移動(dòng)條件及距離進(jìn)行修改適配
- 只有在滾動(dòng)的offset.x為0-40之間才處理手勢(shì)
- 讓滑動(dòng)剛開(kāi)始時(shí),將上一級(jí)的Controller的截圖顯示出來(lái)插入superView里顯示笆檀,好讓有側(cè)滑比較效果
3忌堂、在滾動(dòng)中時(shí),將當(dāng)前navigationController的View的Frame移動(dòng)位置酗洒,同時(shí)設(shè)置上一級(jí)頁(yè)面的截圖的alpha值士修,讓有一個(gè)漸變的效果
4、手勢(shì)結(jié)束是寝蹈,判斷當(dāng)前手勢(shì)所處位置李命,如果超過(guò)50登淘,這自動(dòng)無(wú)動(dòng)畫pop箫老,然后刪除remove背景截圖,同時(shí)讓navigationcontroller的frame的x為0黔州,完整展示之前的Controller
#pragma mark- private method
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer {
if ([[self valueForKey:@"_isTransitioning"] boolValue]) {
return NO;
}
if ([self.navigationController.transitionCoordinator isAnimated]) {
return NO;
}
if (self.childViewControllers.count <= 1) {
return NO;
}
//UIViewController *vc = self.topViewController;
// 側(cè)滑手勢(shì)觸發(fā)位置
CGPoint location = [gestureRecognizer locationInView:self.view];
CGPoint offSet = [gestureRecognizer translationInView:gestureRecognizer.view];
BOOL ret = (0 < offSet.x && location.x <= 40);
// NSLog(@"%@ %@",NSStringFromCGPoint(location),NSStringFromCGPoint(offSet));
return ret;
}
- (void)panGestureRecognizerAction:(UIPanGestureRecognizer*)panGestureRecognizer{
if ([self.viewControllers count] == 1) {
return ;
}
if (panGestureRecognizer.state == UIGestureRecognizerStateBegan) {
NSLog(@"滑動(dòng)開(kāi)始");
//存放滑動(dòng)開(kāi)始的位置
self.panBeginPoint = [panGestureRecognizer locationInView:[UIApplication sharedApplication].keyWindow];
//插入圖片
[self insertLastViewFromSuperView:self.view.superview];
}else if(panGestureRecognizer.state == UIGestureRecognizerStateEnded){
NSLog(@"滑動(dòng)結(jié)束");
//存放數(shù)據(jù)
self.panEndPoint = [panGestureRecognizer locationInView:[UIApplication sharedApplication].keyWindow];
if ((_panEndPoint.x - _panBeginPoint.x) > 50) {
[UIView animateWithDuration:0.3 animations:^{
[self moveNavigationViewWithLenght:[UIScreen mainScreen].bounds.size.width];
} completion:^(BOOL finished) {
[self removeLastViewFromSuperView];
[self moveNavigationViewWithLenght:0];
[self popViewControllerAnimated:NO];
}];
}else{
[UIView animateWithDuration:0.3 animations:^{
[self moveNavigationViewWithLenght:0];
}];
}
}else{
//添加移動(dòng)效果
CGFloat panLength = ([panGestureRecognizer locationInView:[UIApplication sharedApplication].keyWindow].x - _panBeginPoint.x);
if (panLength > 0) {
[self moveNavigationViewWithLenght:panLength];
}
}
}
/**
* 移動(dòng)視圖界面
*
* @param lenght 移動(dòng)的長(zhǎng)度
*/
- (void)moveNavigationViewWithLenght:(CGFloat)lenght{
//圖片位置設(shè)置
self.view.frame = CGRectMake(lenght, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height);
//圖片動(dòng)態(tài)陰影
_backView.alpha = (lenght/[UIScreen mainScreen].bounds.size.width)*2/3 + 0.33;
}
/**
* 插圖上一級(jí)圖片
*
* @param superView 圖片的superView
*/
- (void)insertLastViewFromSuperView:(UIView *)superView{
//插入上一級(jí)視圖背景
if (_backView == nil) {
_backView = [[UIImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
_backView.image = [_backImgs lastObject];;
}
[self.view.superview insertSubview:_backView belowSubview:self.view];
}
/**
* 移除上一級(jí)圖片
*/
- (void)removeLastViewFromSuperView{
[_backView removeFromSuperview];
_backView = nil;
}
- 方案六(個(gè)人覺(jué)得最佳方案)
使用FDFullscreenPopGesture+TZScrollViewPopGesture結(jié)合的方式有效解決一般從noNavBar->noNavBar,noNavBar->hasNavBar會(huì)有跳動(dòng)的感覺(jué)
demo:TestPopGestureSolution6
小結(jié)
方案一和方案二使用自定義對(duì)navigationController.interactivePopGestureRecognizer.delegate進(jìn)行替換處理
方案三和方案四是繼續(xù)使用系統(tǒng)的interactivePopGestureRecognizer.delegate
同時(shí)方案二和方案四都處理了scrollView的側(cè)滑返回問(wèn)題耍鬓,但由于方案二處理scrollView側(cè)滑時(shí)使用繼承重寫,在有些使用第三方時(shí)并不很方便流妻,這一點(diǎn)牲蜀,方案四一句代碼就處理了很方便,就整體代碼而言绅这,感覺(jué)方案四最優(yōu)涣达,但方案二還能自動(dòng)處理有無(wú)導(dǎo)航欄的處理,方案四沒(méi)有涉及這一塊兒证薇,但方案四和方案二是可以混用的度苔,方案二+方案四感覺(jué)是挺不錯(cuò)的選擇
在上面列出的幾種解決方案中,只有FDFullscreenPopGesture很好的處理有沒(méi)有隱藏導(dǎo)航欄的各個(gè)頁(yè)面的切換導(dǎo)致navigationbar閃一下的問(wèn)題浑度,但在處理scrollView相關(guān)的側(cè)滑時(shí)使用的方式不是很優(yōu)雅寇窑,而且對(duì)于一些不能重新定義一個(gè)scrollView子類的頁(yè)面沒(méi)法很好的適配,而TZScrollViewPopGesture則很好的處理了這個(gè)問(wèn)題箩张,但TZScrollViewPopGesture在處理導(dǎo)航欄是否隱藏跳轉(zhuǎn)時(shí)有點(diǎn)兒跳轉(zhuǎn)的突兀甩骏,所以這2個(gè)合起來(lái)是最好的一種處理方式窗市。
全部例子demo NavPOPGestureBack