iOS側(cè)滑pop返回的第三方整理研究

下一篇相關(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)畫

  1. 方法一:
    這篇文章介紹了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
  1. 方法二
    使用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;
}
- (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)行修改適配

  1. 只有在滾動(dòng)的offset.x為0-40之間才處理手勢(shì)
  2. 讓滑動(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;
}

小結(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市饮笛,隨后出現(xiàn)的幾起案子咨察,更是在濱河造成了極大的恐慌,老刑警劉巖福青,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扎拣,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡素跺,警方通過(guò)查閱死者的電腦和手機(jī)二蓝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)指厌,“玉大人刊愚,你說(shuō)我怎么就攤上這事〔妊椋” “怎么了鸥诽?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)箕憾。 經(jīng)常有香客問(wèn)我牡借,道長(zhǎng),這世上最難降的妖魔是什么袭异? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任钠龙,我火速辦了婚禮,結(jié)果婚禮上御铃,老公的妹妹穿的比我還像新娘碴里。我一直安慰自己,他們只是感情好上真,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布咬腋。 她就那樣靜靜地躺著,像睡著了一般睡互。 火紅的嫁衣襯著肌膚如雪根竿。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,337評(píng)論 1 310
  • 那天就珠,我揣著相機(jī)與錄音寇壳,去河邊找鬼。 笑死嗓违,一個(gè)胖子當(dāng)著我的面吹牛九巡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼国觉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼宋雏!你這毒婦竟也來(lái)了爸舒?” 一聲冷哼從身側(cè)響起荚醒,我...
    開(kāi)封第一講書(shū)人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤湘今,失蹤者是張志新(化名)和其女友劉穎岔擂,沒(méi)想到半個(gè)月后病苗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體睬辐,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挠阁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了溯饵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侵俗。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖丰刊,靈堂內(nèi)的尸體忽然破棺而出隘谣,到底是詐尸還是另有隱情,我是刑警寧澤啄巧,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布寻歧,位于F島的核電站,受9級(jí)特大地震影響秩仆,放射性物質(zhì)發(fā)生泄漏码泛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一澄耍、第九天 我趴在偏房一處隱蔽的房頂上張望噪珊。 院中可真熱鬧,春花似錦逾苫、人聲如沸卿城。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至搀捷,卻和暖如春星掰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嫩舟。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工氢烘, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人家厌。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓播玖,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親饭于。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蜀踏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容