iOS-實現(xiàn)安卓左滑返回上一頁面效果

閑來無事昙读,整點活兒干干!
不整別的舌缤,先來巴拉巴拉這玩意兒怎么玩兒箕戳。

開局一張圖,內(nèi)容全靠騙

image

開整

首先需要滑動国撵,那肯定得要來一個滑動的手勢陵吸,既然滑動手勢有了,加哪介牙?既然手勢作為全局生效壮虫,那肯定不能加在單個的view上,所以直接加在window上环础,這樣不就都有了嘛囚似。


UIScreenEdgePanGestureRecognizer *gesture = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:**@selector(screenEdgePanGestureExcuteEvent:)];
gesture.edges = UIRectEdgeRight;
[Gesture_KeyWindow addGestureRecognizer:gesture];

既然手勢加上了,那不得來個響應(yīng)事件线得。

如何去實現(xiàn)呢饶唤,拖動的時候要像臉上的痘痘一樣,冒出個頭贯钩,直接加個view募狂,然后做動畫??,你來角雷,我整不動祸穷,別問我為什么,問就是我這么干過勺三,結(jié)果把路走死了雷滚。
那就想想其他辦法,那不得有個玩意兒叫layer嘛吗坚。那我添加個畫布多刺激祈远,想加啥就加啥。
說干就干商源。

第一步:

既然要拖车份,那就得先拿到位置,對吧炊汹,這個沒啥問題吧(有問題的躬充,請?zhí)岢瞿愕膯栴},我不回答,我怕我打死你)充甚。


CGPoint changePoint = [gesture locationInView:Gesture_KeyWindow];

先忽略細節(jié)以政,開始畫豬,咱得先畫個豬腦袋吧伴找,至少知道你畫的是個啥玩意兒盈蛮,先整個效果都!

點拿到了技矮,那就要開始拖了抖誉。那我們是不是得要知道兩個狀態(tài)啊,我到底是剛摁下去呢衰倦,還是在拖動袒炉,對吧,所以就有倆貨樊零,UIGestureRecognizerStateBegan我磁、UIGestureRecognizerStateChanged,一個是咱剛摁下去的狀態(tài)驻襟,一個是在滑的狀態(tài)夺艰,好了,這兩個狀態(tài)知道了沉衣,在UIGestureRecognizerStateBegan狀態(tài)是記錄開始的點郁副,在UIGestureRecognizerStateChanged狀態(tài)的時候記錄變化點。

開始動用你的腦瓜子YY一下豌习,咱拖的時候不是有一個痘痘一樣凸起的東西嗎存谎?那咱先畫那個玩意兒。想想那個S一樣的曲線斑鸦,腦瓜一熱愕贡,這不就來了嗎草雕,UIBezierPath這貨巷屿,只要你牛逼,啥畫不出來墩虹,看看它的方法


- (void)moveToPoint:(CGPoint)point;

- (void)addLineToPoint:(CGPoint)point;

- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;

- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;

- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwiseAPI_AVAILABLE(ios(4.0));

- (void)closePath;

第一個移到一個點嘱巾,這不難理解,既然要畫那不得知道從那開始诫钓,對吧旬昭!
第二個添加一條線,還是直的菌湃,想想那個曲線问拘,明明是彎的好吧,所以pass掉
第三個畫的是彎的線,大致就是本來是直的線段骤坐,現(xiàn)在來了倆點绪杏,然后按照切線的方向把線往一邊扯,然后彎了
第四個和第三個一樣纽绍,不過是兩個點變成了一個
第五個蕾久,不管了,第六個把最后的起點和終點鏈接起來

方法的意思知道了拌夏,那就開始描線了
首先起點位置選擇僧著,我們拿到拖動的點,那這個點的y肯定是線的中間位置(別問為什么障簿,問就是只可意會不可言傳)盹愚,線的中間點知道了,那就可以定一個起點和終點的y值站故,跨度多少呢杯拐,經(jīng)過不斷嘗試,大概200的跨度差不多世蔗,所以起點的y值就是changePoint.y - 100端逼,終點就是changePoint + y。然后開始的x污淋,既然從邊緣開始顶滩,那不就是屏幕的寬度嘛,so easy寸爆,所以起點的位置就是{Screen_Width, changePoint.y - 100}礁鲁,終點就是{Screen_Width, changePoint.y + 100}
所以

第二步

移動到一個點 (想啥呢,layerUIBezierPath都還沒創(chuàng)建呢)


shapeLayer = [CAShapeLayer layer];
shapeLayer.backgroundColor = [UIColor clearColor].CGColor;
shapeLayer.frame = CGRectMake(0, 0, Gesture_SCREEN_WIDTH, Gesture_SCREEN_HEIGHT);
shapeLayer.lineJoin = kCALineJoinRound;
shapeLayer.lineCap = kCALineCapRound;

創(chuàng)建UIBezierPath赁豆,開始畫線


UIBezierPath *path = [[UIBezierPath alloc] init];
path.lineWidth=1;
path.usesEvenOddFillRule = YES;
[path moveToPoint:CGPointMake(Gesture_SCREEN_WIDTH, tempPoint.y - 100)];

然后想想那個曲線仅醇,分解一下,第一段像是一個丿魔种,第二段像是一個(析二,第三段像一個la(不好意思沒找到),那不就來了嘛节预,小老弟叶摄,直接上代碼(想屁吃呢,點還沒確定呢)

首先確定第一段終點位置安拟,要丿到一個地方蛤吓,首先y的位置肯定不能是拖動點的y的位置,那玩意兒是中心點糠赦,肯定比它要高会傲,那就高個20吧(別問锅棕,問就是大概加估計)。
再開始確定x的位置淌山,首先比拖動點的x要大(為什么大哲戚,從左到右,0~屏幕寬度艾岂,你說為啥大)顺少,然后大多少,憑借我這幾百度的近視眼估計了一下王浴,大概是拖動長度的四分之一就夠了脆炎,那不就來了嘛,x就是tempPoint.x + 10* (Gesture_SCREEN_WIDTH - tempPoint.x) / 40氓辣,(有沒有不知道的秒裕,不知道算了),第一段終點位置就確定了{tempPoint.x + 10 * (Gesture_SCREEN_WIDTH - tempPoint.x) / 40, tempPoint.y - 20}tempPoint也就是拖動點钞啸,不過做了一些處理)

終點確定了几蜻,那就開始確定往哪個點彎,經(jīng)過不斷試錯我來確定了一個(Gesture_SCREEN_WIDTH, tempPoint.y - 70)体斩,不多解釋梭稚,解釋就是憑經(jīng)驗(錯誤的經(jīng)驗),所以代碼就是


[path addQuadCurveToPoint:CGPointMake(tempPoint.x+10* (Gesture_SCREEN_WIDTH- tempPoint.x) /40, tempPoint.y-20) controlPoint:CGPointMake(Gesture_SCREEN_WIDTH, tempPoint.y-70)];

第二段就簡單了絮吵,對稱一下弧烤,就拿到終點{tempPoint.x + 10 * (Gesture_SCREEN_WIDTH - tempPoint.x) / 40, tempPoint.y + 20}x不變蹬敲,起點y20暇昂,終點就加20,控制點根據(jù)第一個點的方法判斷x就是tempPoint.x - 10 * (Gesture_SCREEN_WIDTH - tempPoint.x) / 40伴嗡,y自然就是中心點了急波,控制點就是{tempPoint.x - 10 * (Gesture_SCREEN_WIDTH - tempPoint.x) / 40, tempPoint.y},第三段依葫蘆畫瓢瘪校,和第一段一樣澄暮,三段畫完,關(guān)門


[path addQuadCurveToPoint:CGPointMake(tempPoint.x + 10 * (Gesture_SCREEN_WIDTH - tempPoint.x) / 40, tempPoint.y + 20) controlPoint:CGPointMake(tempPoint.x - 10 * (Gesture_SCREEN_WIDTH - tempPoint.x) / 40, tempPoint.y)];
[path addQuadCurveToPoint:CGPointMake(Gesture_SCREEN_WIDTH, tempPoint.y + 100) controlPoint:CGPointMake(Gesture_SCREEN_WIDTH, tempPoint.y + 70)];
[path closePath];

然后給layer填充顏色渣淤,曲線添加到layer上赏寇,perfect吉嫩!


shapeLayer.fillColor = [_config.backGroundColor colorWithAlphaComponent:_config.backGroundAlpha].CGColor;
shapeLayer.path = path.CGPath;
[Gesture_KeyWindow.layer addSublayer:shapeLayer];

第三步:

背景畫好了价认,那不得再來個圖片點綴一下?
再來個圖片的layer自娩,添加到剛剛的layer
至于位置嘛用踩,不就跟痘痘里的東西一樣嘛渠退,對吧,所以直接就是{tempPoint.x + 10, tempPoint.y - 15, 30, 30}
話不多說脐彩,直接上代碼碎乃。看不懂算求


CALayer *subLayer = [CALayer layer];
subLayer.backgroundColor = [UIColor clearColor].CGColor;
subLayer.contents = (__bridge id _Nullable)([self changeImage:Gesture_ImageWithName(_config.returnImageName) Color:_config.imageColor].CGImage);
subLayer.frame = CGRectMake(tempPoint.x+10, tempPoint.y - 15, 30, 30);

至此惠奸,背景大功告成

第四步:

開始添磚加瓦
拖動的時候梅誓,不能無限拖唄
所以得限制一下,如果拖動到某個邊界的時候佛南,繼續(xù)拖就不要改變x值了梗掰,我設(shè)置了最大位移距離為40


CGPoint tempPoint = CGPointZero;
if (progressPoint.x < Gesture_SCREEN_WIDTH - 40) {
    tempPoint = CGPointMake(Gesture_SCREEN_WIDTH - 40, progressPoint.y);
}else{ 
    tempPoint = progressPoint;
}

既然x設(shè)置限制,y有沒有呢嗅回,當然也有及穗。當我們在屏幕頂部或底部的時候,起點和終點是不是就可能會超出屏幕了绵载,所以限制一下y的值埂陆,最小不得小于100(為什么是100,看上面)娃豹,最大不能超過屏幕高度 - 100焚虱。


if (progressPoint.y <= 100) {
    tempPoint = CGPointMake(tempPoint.x,100)
}else if (progressPoint.y >= Gesture_SCREEN_HEIGHT - 100) {
    tempPoint = CGPointMake(tempPoint.x,Gesture_SCREEN_HEIGHT - 100);
 }

既然那個拖動展示的曲線要根據(jù)我當前的位置,那我是不是只要我接觸點改變懂版,就得重新繪制一次
另外就是直接返回首頁著摔,當我拖到一定位置時,幾秒中不動就應(yīng)該觸發(fā)返回首頁的操作定续,但是5亍!私股!不動摹察?,屏幕感知那么靈敏倡鲸,呼吸一下都有細微差距供嚎,所以不動,指的是宏觀上的不動峭状,不要在意細小的差別克滴,所以給個左右精度就好了,我這兒設(shè)置的是左右不超過3就算為不動
所以思路有优床,那就簡單了劝赔,上才藝,上代碼


CGPoint changePoint = [gesture locationInView:Gesture_KeyWindow];
if (gesture.state == UIGestureRecognizerStateBegan) {
     farPoint= changePoint;
}
if (gesture.state == UIGestureRecognizerStateChanged) {        
     if (changePoint.x < farPoint.x)
         farPoint= changePoint;
     }
     if (_config.isCanPopToRootViewController) {
        if (homeLeftPoint.x > changePoint.x + 3 || homeLeftPoint.x < changePoint.x -3 ) {
             [self createLayer:changePoint];
             isPopToRootController = NO;
             [NSObject cancelPreviousPerformRequestsWithTarget:self];
             [self performSelector:@selector(changeReturnType:) withObject:@(changePoint.x) afterDelay:_config.returnHomeTime];
        }
     }else{
         [self createLayer:changePoint];
     }
}


/**
 改變返回執(zhí)行類型以及圖標
 */
- (void)changeReturnType:(NSNumber *)pointX {
    homeLeftPoint = CGPointMake([pointX floatValue],0);
    isPopToRootController = YES;
    CALayer *layer = [shapeLayer.sublayers firstObject];
    layer.contents = (__bridge id _Nullable)([self changeImage:Gesture_ImageWithName(_config.returnHomeImageName) Color:_config.homeImageColor].CGImage);
}

最后就是松手的時候了胆敞,當手勢狀態(tài)為UIGestureRecognizerStateEnded着帽,即是松手了杂伟。
那不得簡簡單單,直接調(diào)用返回不就完了仍翰,想屁吃呢赫粥,那個痘痘不是還展示在屏幕嗎?不打算給摁下去予借?
所以下一步操作來了越平,讓這個痘痘圓潤的滾回去
所以咱怎么拖出來的,怎么給收回去不就好了嘛灵迫!
但是P省!9暝佟J檎ⅰ!我不想搞了利凑,你們玩兒吧浆劲,我直接簡單粗暴一點,給個動畫哀澈,直接他移出去牌借,雖然效果沒有重新繪制好,但是誰叫我懶呢割按。上代碼


if (gesture.state == UIGestureRecognizerStateEnded) {
        CABasicAnimation *anim = [CABasicAnimation animation];
        anim.keyPath=@"position";
        anim.duration=0.5;
        anim.fromValue = [NSValue valueWithCGPoint:shapeLayer.position];
        anim.toValue = [NSValue valueWithCGPoint:CGPointMake(shapeLayer.position.x + 55, shapeLayer.position.y)];
        anim.fillMode = kCAFillModeForwards;
        anim.removedOnCompletion = NO;
        [shapeLayer addAnimation:anim forKey:nil];
        if (changePoint.x < farPoint.x + 20)
            if  (!isPopToRootController) {
                if (self.delegate && [self.delegate respondsToSelector:@selector(CM_GestureRecognizerPopLastController:currentController:)]) {
                    UIViewController*lastController =nil;
                    NSInteger controllerCount =_config.navigationController.viewControllers.count;
                    if (controllerCount >= 2) {
                        lastController =_config.navigationController.viewControllers[controllerCount -2];
                    }
                    if ([self.delegate CM_GestureRecognizerPopLastController:lastController currentController:_config.navigationController.topViewController]) {
                        [_config.navigationController popViewControllerAnimated:_config.isShowPopAnimated];
                    }
                }else{
                    [_config.navigationController popViewControllerAnimated:_config.isShowPopAnimated];
                }
            }else{
                if (self.delegate && [self.delegate respondsToSelector:@selector(CM_GestureRecognizerBackHomeController:)]) {
                    if ([self.delegate CM_GestureRecognizerBackHomeController:_config.navigationController.viewControllers.firstObject]) {
                        [_config.navigationController popToRootViewControllerAnimated:_config.isShowPopAnimated];
                    }
                }else{
                    [_config.navigationController popToRootViewControllerAnimated:_config.isShowPopAnimated];
                }
            }
        }
    }

用法

- (void)initScreenPoP {
    PopGestureRecognizerManager *manager = [PopGestureRecognizerManager shareManager];
    //設(shè)置返回圖片
    manager.config.returnImageName = @"icon_pop_jt";
    //設(shè)置返回代理膨报,可不設(shè)置,不設(shè)置默認返回
    //代理的返回值控制是否自動調(diào)用返回
    manager.delegate = self;
    //設(shè)置返回首頁圖片
    manager.config.returnHomeImageName = @"icon_pop_home";
    //config傳nil适荣,就使用默認的配置现柠,也可以自各兒創(chuàng)建
    //頁面可單獨修改manager.config的各個配置
    //根控制器默認不觸發(fā)返回效果
    [manager registerManagerWithConfig:nil completeBlock:^(BOOL isSuccess, NSString *failString) {
          NSLog(@"%@",failString);
    }];
}

附所有代碼
PopGestureRecognizerManager.h

//
//  PopGestureRecognizerManager.h
//  ScreenPoP
//
//  Created by wr on 2019/7/3.
//  Copyright ? 2019年 wanmengchao. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "PopGestureRecognizerManagerConfiger.h"
#import "PopGestureRecognizerDelegate.h"

NS_ASSUME_NONNULL_BEGIN

@interface PopGestureRecognizerManager : NSObject

/// 配置
@property (nonatomic, strong, nonnull) PopGestureRecognizerManagerConfiger *config;

/// 返回代理
@property (nonatomic, weak) id<PopGestureRecognizerDelegate> delegate;

/// 單例
+ (instancetype)shareManager;

/// 注冊
/// @param config 配置信息
/// @param block 注冊回調(diào)
- (void)registerManagerWithConfig:(PopGestureRecognizerManagerConfiger * __nullable)config completeBlock:(void(^ __nullable)(BOOL isSuccess, NSString *failString))block;
@end

NS_ASSUME_NONNULL_END

PopGestureRecognizerManager.m

//
//  PopGestureRecognizerManager.m
//  ScreenPoP
//
//  Created by wr on 2019/7/3.
//  Copyright ? 2019年 wanmengchao. All rights reserved.
//

#import "PopGestureRecognizerManager.h"

//屏幕大小
#define Gesture_SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define Gesture_SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
//顏色宏定義
#define Gesture_COLOR_HEX(hex) Gesture_COLOR_HEXA(hex,1.0f)
#define Gesture_COLOR_HEXA(rgbValue,a) [UIColor colorWithRed:((float)(((rgbValue) & 0xFF0000) >> 16))/255.0 green:((float)(((rgbValue) & 0xFF00)>>8))/255.0 blue: ((float)((rgbValue) & 0xFF))/255.0 alpha:(a)]
#define Gesture_ImageWithName(imgName) [UIImage imageNamed:imgName]
#define Gesture_KeyWindow [[UIApplication sharedApplication] delegate].window
#define PPLog(format, ...) printf("%s",[[NSString stringWithFormat:(format), ##__VA_ARGS__] UTF8String])

static PopGestureRecognizerManager *manager = nil;

@implementation PopGestureRecognizerManager{
    CAShapeLayer *shapeLayer;
    CGPoint startPoint;
    CGPoint farPoint;
    CGPoint lastPoint;
    CGPoint homeLeftPoint;
    BOOL isPopToRootController;
    NSTimer *timer;
}

+ (instancetype)shareManager {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[PopGestureRecognizerManager alloc] init];
    });
    return manager;
}


/// 重寫單例對象的alloc方法, 防止單例對象被重復(fù)創(chuàng)建
+ (instancetype)alloc {
    if (manager) {
        // 如果單例對象存在則拋出異常
        NSException *exception = [NSException exceptionWithName:@"重復(fù)創(chuàng)建單例對象異常" reason:@"單例被重復(fù)創(chuàng)建" userInfo:nil];
        [exception raise];
    }
    return [super alloc];
}

- (instancetype)init {
    if (self = [super init]) {
        _config = [[PopGestureRecognizerManagerConfiger alloc] init];
    }
    return self;
}

/**
 添加手勢到window上
 */
- (void)addScreenEdgePanGestureToWindow {
    UIScreenEdgePanGestureRecognizer *gesture = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(screenEdgePanGestureExcuteEvent:)];
    gesture.edges = UIRectEdgeRight;
    [Gesture_KeyWindow addGestureRecognizer:gesture];
}

- (void)registerManagerWithConfig:(PopGestureRecognizerManagerConfiger * __nullable)config completeBlock:(void(^ __nullable)(BOOL isSuccess, NSString *failString))block {
    if (config) {
        self.config = config;
    }
    [self addScreenEdgePanGestureToWindow];
    if (self.config.returnImageName.length == 0) {
        if (block) {
            block(YES, @"拖動時返回的圖片未設(shè)置(returnImageName未設(shè)置)");
        }
    }if (self.config.isCanPopToRootViewController && self.config.returnHomeImageName.length == 0) {
        if (block) {
            block(YES, @"可以返回主頁時(isCanPopToRootViewController = YES),拖動展示的圖片未設(shè)置(returnHomeImageName未設(shè)置)");
        }
    }else{
        if (block) {
            block(YES, @"當前controller為導(dǎo)航控制器的根控制器時弛矛,拖動無效果");
        }
    }
    
#if DEBUG
    PPLog(@"==========Manager注冊信息==========\n");
    PPLog(@"NavigationController:%@\n",self.config.navigationController);
    PPLog(@"拖動時的背景顏色R:%g G:%g B:%g \n",CGColorGetComponents(self.config.backGroundColor.CGColor)[0] * 255,CGColorGetComponents(self.config.backGroundColor.CGColor)[1] * 255,CGColorGetComponents(self.config.backGroundColor.CGColor)[2] * 255);
    PPLog(@"背景顏色Alpha值:%g\n",self.config.backGroundAlpha);
    PPLog(@"拖動時展示的圖片:%@\n",self.config.returnImageName);
    if (self.config.imageColor) {
        PPLog(@"拖動時圖片的顏色:R:%g G:%g B:%g \n",CGColorGetComponents(self.config.imageColor.CGColor)[0] * 255,CGColorGetComponents(self.config.imageColor.CGColor)[1] * 255,CGColorGetComponents(self.config.imageColor.CGColor)[2] * 255);
    }else{
        PPLog(@"不改變拖動時圖片的顏色\n");
    }
    PPLog(@"是否跟隨手勢位置移動:%@\n",self.config.isFollowGesturePosition ? @"是" : @"否");
    PPLog(@"是否可以返回首頁:%@\n",self.config.isCanPopToRootViewController ? @"是" : @"否");
    PPLog(@"返回首頁拖動時展示的圖片:%@\n",self.config.returnImageName);
    PPLog(@"返回首頁拖動觸發(fā)時間:%g\n",self.config.returnHomeTime);
    PPLog(@"是否展示返回動畫:%@\n",self.config.isShowPopAnimated ? @"是" : @"否");
#endif
}

/**
 屏幕邊界手勢執(zhí)行事件
 
 @param gesture 手勢
 */
- (void)screenEdgePanGestureExcuteEvent:(UIScreenEdgePanGestureRecognizer *)gesture {
    if (_config.navigationController.viewControllers.count == 1) {
        return;
    }
    CGPoint changePoint = [gesture locationInView:Gesture_KeyWindow];
    if (gesture.state == UIGestureRecognizerStateBegan) {
        farPoint = changePoint;
    }
    if (gesture.state == UIGestureRecognizerStateChanged) {
        if (changePoint.x < farPoint.x) {
            farPoint = changePoint;
        }
        if (_config.isCanPopToRootViewController) {
            if (homeLeftPoint.x > changePoint.x + 3 || homeLeftPoint.x < changePoint.x - 3) {
                [self createLayer:changePoint];
                isPopToRootController = NO;
                [NSObject cancelPreviousPerformRequestsWithTarget:self];
                [self performSelector:@selector(changeReturnType:) withObject:@(changePoint.x) afterDelay:_config.returnHomeTime];
            }
        }else{
            [self createLayer:changePoint];
        }
    }
    if (gesture.state == UIGestureRecognizerStateEnded) {
        CABasicAnimation *anim = [CABasicAnimation animation];
        anim.keyPath = @"position";
        anim.duration = 0.5;
        anim.fromValue = [NSValue valueWithCGPoint:shapeLayer.position];
        anim.toValue = [NSValue valueWithCGPoint:CGPointMake(shapeLayer.position.x + 55, shapeLayer.position.y)];
        anim.fillMode = kCAFillModeForwards;
        anim.removedOnCompletion = NO;
        [shapeLayer addAnimation:anim forKey:nil];
        if (changePoint.x < farPoint.x + 20) {
            if (!isPopToRootController) {
                if (self.delegate && [self.delegate respondsToSelector:@selector(CM_GestureRecognizerPopLastController:currentController:)]) {
                    UIViewController *lastController = nil;
                    NSInteger controllerCount = _config.navigationController.viewControllers.count;
                    if (controllerCount >= 2) {
                        lastController = _config.navigationController.viewControllers[controllerCount - 2];
                    }
                    if ([self.delegate CM_GestureRecognizerPopLastController:lastController currentController:_config.navigationController.topViewController]) {
                        [_config.navigationController popViewControllerAnimated:_config.isShowPopAnimated];
                    }
                }else{
                    [_config.navigationController popViewControllerAnimated:_config.isShowPopAnimated];
                }
            }else{
                if (self.delegate && [self.delegate respondsToSelector:@selector(CM_GestureRecognizerBackHomeController:)]) {
                    if ([self.delegate CM_GestureRecognizerBackHomeController:_config.navigationController.viewControllers.firstObject]) {
                        [_config.navigationController popToRootViewControllerAnimated:_config.isShowPopAnimated];
                    }
                }else{
                    [_config.navigationController popToRootViewControllerAnimated:_config.isShowPopAnimated];
                }
            }
        }
    }
}

/**
 創(chuàng)建背景l(fā)ayer
 
 @param progressPoint 滑動點
 */
- (void)createLayer:(CGPoint)progressPoint {
    CGPoint tempPoint = CGPointZero;
    if (progressPoint.x < Gesture_SCREEN_WIDTH - 40) {
        tempPoint = CGPointMake(Gesture_SCREEN_WIDTH - 40, progressPoint.y);
    }else{
        tempPoint = progressPoint;
    }
    if (_config.isFollowGesturePosition) {
        if (progressPoint.y <= 100) {
            tempPoint = CGPointMake(tempPoint.x, 100);
        }else if (progressPoint.y >= Gesture_SCREEN_HEIGHT - 100) {
            tempPoint = CGPointMake(tempPoint.x, Gesture_SCREEN_HEIGHT - 100);
        }
    }else{
        tempPoint = CGPointMake(tempPoint.x, Gesture_SCREEN_HEIGHT / 2);
    }
    [shapeLayer removeFromSuperlayer];
    shapeLayer = [CAShapeLayer layer];
    shapeLayer.backgroundColor = [UIColor clearColor].CGColor;
    shapeLayer.frame = CGRectMake(0, 0, Gesture_SCREEN_WIDTH, Gesture_SCREEN_HEIGHT);
    shapeLayer.lineJoin = kCALineJoinRound;
    shapeLayer.lineCap = kCALineCapRound;
    CALayer *subLayer = [CALayer layer];
    subLayer.backgroundColor = [UIColor clearColor].CGColor;
    subLayer.contents = (__bridge id _Nullable)([self changeImage:Gesture_ImageWithName(_config.returnImageName) Color:_config.imageColor].CGImage);
    subLayer.frame = CGRectMake(tempPoint.x + 10, tempPoint.y - 15, 30, 30);
    UIBezierPath *path = [[UIBezierPath alloc] init];
    path.lineWidth = 1;
    path.usesEvenOddFillRule = YES;
    [path moveToPoint:CGPointMake(Gesture_SCREEN_WIDTH, tempPoint.y - 100)];
    [path addQuadCurveToPoint:CGPointMake(tempPoint.x + 10 * (Gesture_SCREEN_WIDTH - tempPoint.x) / 40, tempPoint.y - 20) controlPoint:CGPointMake(Gesture_SCREEN_WIDTH, tempPoint.y - 70)];
    [path addQuadCurveToPoint:CGPointMake(tempPoint.x + 10 * (Gesture_SCREEN_WIDTH - tempPoint.x) / 40, tempPoint.y + 20) controlPoint:CGPointMake(tempPoint.x - 0 * (Gesture_SCREEN_WIDTH - tempPoint.x) / 40, tempPoint.y)];
    [path addQuadCurveToPoint:CGPointMake(Gesture_SCREEN_WIDTH, tempPoint.y + 100) controlPoint:CGPointMake(Gesture_SCREEN_WIDTH, tempPoint.y + 70)];
    [path closePath];
    shapeLayer.fillColor = [_config.backGroundColor colorWithAlphaComponent:_config.backGroundAlpha].CGColor;
    shapeLayer.path = path.CGPath;
    [shapeLayer addSublayer:subLayer];
    [Gesture_KeyWindow.layer addSublayer:shapeLayer];
}

/**
 改變返回執(zhí)行類型以及圖標
 */
- (void)changeReturnType:(NSNumber *)pointX {
    homeLeftPoint = CGPointMake([pointX floatValue], 0);
    isPopToRootController = YES;
    CALayer *layer = [shapeLayer.sublayers firstObject];
    layer.contents = (__bridge id _Nullable)([self changeImage:Gesture_ImageWithName(_config.returnHomeImageName) Color:_config.homeImageColor].CGImage);
}

/**
 修改圖片顏色
 
 @param image 要改變的圖片
 @param color 設(shè)置的顏色
 @return 返回圖片
 */
- (UIImage *)changeImage:(UIImage *)image Color:(UIColor * __nullable)color {
    if (color) {
        UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextTranslateCTM(context, 0, image.size.height);
        CGContextScaleCTM(context, 1.0, -1.0);
        CGContextSetBlendMode(context, kCGBlendModeNormal);
        CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
        CGContextClipToMask(context, rect, image.CGImage);
        [color setFill];
        CGContextFillRect(context, rect);
        UIImage*newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }else{
        return image;
    }
}

@end


PopGestureRecognizerManagerConfiger.h

//
//  PopGestureRecognizerManagerConfiger.h
//  ScreenPoP
//
//  Created by wr on 2019/7/3.
//  Copyright ? 2019年 wanmengchao. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface PopGestureRecognizerManagerConfiger : NSObject

/**
 導(dǎo)航控制器
 默認為當前window的導(dǎo)航控制器
 */
@property (nonatomic, strong, nullable) UINavigationController *navigationController;

/**
 拖動時够吩,背景顏色
 默認為0x999999
 */
@property (nonatomic, strong, nullable) UIColor *backGroundColor;

/**
 背景顏色alpha
 默認為0.5
 */
@property (nonatomic, assign) CGFloat backGroundAlpha;

/**
 拖動時展示的圖片
 必填,否則無圖片
 */
@property (nonatomic, copy, nonnull) NSString *returnImageName;

/**
 用于修改拖動時展示的圖片的顏色
 默認為不變色即nil
 */
@property (nonatomic, retain, nullable) UIColor *imageColor;

/**
 用于修改拖動時展示返回首頁的圖片的顏色
 默認為不變色即nil
 */
@property (nonatomic, retain, nullable) UIColor *homeImageColor;

/**
 是否跟隨手勢位置移動
 默認為YES
 */
@property (nonatomic, assign) BOOL isFollowGesturePosition;

/**
 是否可以返回首頁
 默認為YES
 */
@property (nonatomic, assign) BOOL isCanPopToRootViewController;


/**
 返回首頁拖動時展示的圖片
 isCanPopToRootViewController為YES時丈氓,必填
 */
@property (nonatomic, copy, nullable) NSString *returnHomeImageName;

/**
 返回首頁用時
 默認為1秒
 */
@property (nonatomic, assign) CGFloat returnHomeTime;

/**
 是否執(zhí)行返回動畫
 默認為:YES
 */
@property (nonatomic, assign) BOOL isShowPopAnimated;

@end

NS_ASSUME_NONNULL_END

PopGestureRecognizerManagerConfiger.m

//
//  PopGestureRecognizerManagerConfiger.m
//  ScreenPoP
//
//  Created by wr on 2019/7/3.
//  Copyright ? 2019年 wanmengchao. All rights reserved.
//

#import "PopGestureRecognizerManagerConfiger.h"
#define Gesture_COLOR_HEX(hex) Gesture_COLOR_HEXA(hex,1.0f)
#define Gesture_COLOR_HEXA(rgbValue,a) [UIColor colorWithRed:((float)(((rgbValue) & 0xFF0000) >> 16))/255.0 green:((float)(((rgbValue) & 0xFF00)>>8))/255.0 blue: ((float)((rgbValue) & 0xFF))/255.0 alpha:(a)]

@implementation PopGestureRecognizerManagerConfiger

- (instancetype)init {
    if (self = [super init]) {
        _backGroundColor = Gesture_COLOR_HEX(0x999999);
        _isFollowGesturePosition = YES;
        _isCanPopToRootViewController = YES;
        _isShowPopAnimated = YES;
        _imageColor = nil;
        _returnHomeTime = 1.0f;
        _backGroundAlpha = 0.3f;
    }
    return self;
}

- (UINavigationController *)navigationController {
    if (!_navigationController) {
        _navigationController = (UINavigationController *)[UIApplication sharedApplication].keyWindow.rootViewController;
    }
    return _navigationController;
}


@end

PopGestureRecognizerDelegate.h

//
//  PopGestureRecognizerDelegate.h
//  ScreenPoP
//
//  Created by 萬孟超 on 2022/1/19.
//  Copyright ? 2022 wanmengchao. All rights reserved.
//

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@protocol PopGestureRecognizerDelegate <NSObject>

@optional
/// 返回時代理
/// 重寫返回按鈕時周循,可實現(xiàn)此方法處理數(shù)據(jù)
/// @param lastController 上一個controller
/// @param currentController 當前controller
/// @return 是否自動返回上一個controller
- (BOOL)CM_GestureRecognizerPopLastController:(UIViewController *)lastController currentController:(UIViewController *)currentController;

/// 返回首頁代理
/// @param currentController 當前controller
/// @return 是否自動返回主頁
- (BOOL)CM_GestureRecognizerBackHomeController:(UIViewController *)currentController;

@end

NS_ASSUME_NONNULL_END

至此,搞定收工

附demo地址:ScreenPop: 仿Android左滑返回
Swift版的就不搞了万俗,實現(xiàn)思路一樣的湾笛!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市闰歪,隨后出現(xiàn)的幾起案子嚎研,更是在濱河造成了極大的恐慌,老刑警劉巖课竣,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘉赎,死亡現(xiàn)場離奇詭異置媳,居然都是意外死亡于樟,警方通過查閱死者的電腦和手機公条,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來迂曲,“玉大人靶橱,你說我怎么就攤上這事÷放酰” “怎么了关霸?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長杰扫。 經(jīng)常有香客問我队寇,道長,這世上最難降的妖魔是什么章姓? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任佳遣,我火速辦了婚禮,結(jié)果婚禮上凡伊,老公的妹妹穿的比我還像新娘零渐。我一直安慰自己,他們只是感情好系忙,可當我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布诵盼。 她就那樣靜靜地躺著,像睡著了一般银还。 火紅的嫁衣襯著肌膚如雪风宁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天蛹疯,我揣著相機與錄音杀糯,去河邊找鬼。 笑死苍苞,一個胖子當著我的面吹牛固翰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播羹呵,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼骂际,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了冈欢?” 一聲冷哼從身側(cè)響起歉铝,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎凑耻,沒想到半個月后太示,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柠贤,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年类缤,在試婚紗的時候發(fā)現(xiàn)自己被綠了臼勉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡餐弱,死狀恐怖宴霸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情膏蚓,我是刑警寧澤瓢谢,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站驮瞧,受9級特大地震影響氓扛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜论笔,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一采郎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧翅楼,春花似錦尉剩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至管嬉,卻和暖如春皂林,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蚯撩。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工础倍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人胎挎。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓沟启,卻偏偏與公主長得像,于是被迫代替她去往敵國和親犹菇。 傳聞我的和親對象是個殘疾皇子德迹,可洞房花燭夜當晚...
    茶點故事閱讀 45,455評論 2 359