iOS-返回手勢之無侵入性新增

weixin_34249678發表於2018-01-08

首先, 起因就是被要求為一個已經開發並上線的專案新增返回手勢, 因為本身並不熟悉專案, 所有必須使用無侵入性的方法去實現, 實現的方案也是千千萬, 只是說明一下我所使用的方法, 自認為超級簡潔和方便。

1.runtime新增手勢給UIViewcontroller

最先想到的就是利用runtime新增手勢給UIViewcontroller, 而且系統自帶側滑手勢的回撥方法handleNavigationTransition:,我們在自己的手勢上直接用它的回撥方法, 大大減少了我們的程式碼量和複雜度。

#import "UIViewController+popGesture.h"


@implementation UIViewController (popGesture)

+ (void)load{
    [super load];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 假如要開啟controller的統計 ,則把下面這行程式碼開啟
        __gbh_tracer_swizzleMethod([self class], @selector(loadView), @selector(newLoadView));
    });
}

- (void)newLoadView {
    [self newLoadView];
        
    id target = self.navigationController.interactivePopGestureRecognizer.delegate;
    
    // handleNavigationTransition:為系統私有API,即系統自帶側滑手勢的回撥方法,我們在自己的手勢上直接用它的回撥方法
    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:target action:@selector(handleNavigationTransition:)];
    panGesture.delegate = self; // 設定手勢代理,攔截手勢觸發
    [self.view addGestureRecognizer:panGesture];
    
    // 一定要禁止系統自帶的滑動手勢
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}



// 什麼時候呼叫,每次觸發手勢之前都會詢問下代理方法,是否觸發
// 作用:攔截手勢觸發
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    // 當前控制器是根控制器時,不可以側滑返回,所以不能使其觸發手勢
    if(self.navigationController.childViewControllers.count == 1)
    {
        return NO;
    }
    
    return YES;
}

//// 允許多個手勢併發
//- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
//    return YES;
//}



// 交換方法
void __gbh_tracer_swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector){
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    
    BOOL didAddMethod =
    class_addMethod(class,
                    originalSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));
    
    if (didAddMethod) {
        class_replaceMethod(class,
                            swizzledSelector,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

@end

這樣就實現了介面返回,是不是很簡單,不過這樣只能實現UIViewController介面的返回,在擁有UITableView和UIScrollview時就失效了,這是為什麼呢?
因為我們的返回程式碼是載入在UIViewController上,並沒有為UIScrollview新增,返回的手勢並不能傳遞到UIViewController,所以就失效了

2.處理滾動檢視手勢問題

為了無侵入依然使用分類的方法,依然我們已經分析到原因,只需要滾動檢視將我們需要的返回手勢傳遞下去即可

#import "UIScrollView+popGesture.h"

@implementation UIScrollView (popGesture)

#define IPHONE_H [UIScreen mainScreen].bounds.size.height //螢幕的高度
#define IPHONE_W [UIScreen mainScreen].bounds.size.width // 螢幕的寬度

// 手勢事件會一直往下傳遞,不論當前層次是否對該事件進行響應。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    
    if ([self panBack:gestureRecognizer]) {
        return YES;
    }
    return NO;
}

//location_X可自己定義,其代表的是滑動返回距左邊的有效長度
- (BOOL)panBack:(UIGestureRecognizer *)gestureRecognizer {
    
    //是滑動返回距左邊的有效長度 左1/8有效區域
    int location_X = 0.125 * IPHONE_W;
    
    if (gestureRecognizer == self.panGestureRecognizer) {
        UIPanGestureRecognizer *pan = (UIPanGestureRecognizer *)gestureRecognizer;
        CGPoint point = [pan velocityInView:pan.view];
        UIGestureRecognizerState state = gestureRecognizer.state;
        if (UIGestureRecognizerStateBegan == state || UIGestureRecognizerStatePossible == state) {
            CGPoint location = [gestureRecognizer locationInView:self];

            if (point.x > 0 && location.x < location_X && self.contentOffset.x <= 0) {
                return YES;
            }
        }
    }
    return NO;
    
}


- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    
    if ([self panBack:gestureRecognizer]) {
        return NO;
    }
    return YES;
    
}

這樣任何滾動檢視都實現了滑動返回,是不是很簡單,雖然不會寫作,但是都是乾貨,覺得有用就給我個贊吧,你的贊就是我的動力。

相關文章