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

閑來無事,整點(diǎn)活兒干干!
不整別的,先來巴拉巴拉這玩意兒怎么玩兒。

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

image

開整

首先需要滑動(dòng),那肯定得要來一個(gè)滑動(dòng)的手勢(shì),既然滑動(dòng)手勢(shì)有了,加哪?既然手勢(shì)作為全局生效,那肯定不能加在單個(gè)的view上,所以直接加在window上,這樣不就都有了嘛。


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

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

如何去實(shí)現(xiàn)呢,拖動(dòng)的時(shí)候要像臉上的痘痘一樣,冒出個(gè)頭,直接加個(gè)view,然后做動(dòng)畫??,你來,我整不動(dòng),別問我為什么,問就是我這么干過,結(jié)果把路走死了。
那就想想其他辦法,那不得有個(gè)玩意兒叫layer嘛。那我添加個(gè)畫布多刺激,想加啥就加啥。
說干就干。

第一步:

既然要拖,那就得先拿到位置,對(duì)吧,這個(gè)沒啥問題吧(有問題的,請(qǐng)?zhí)岢瞿愕膯栴},我不回答,我怕我打死你)。


CGPoint changePoint = [gesture locationInView:Gesture_KeyWindow];

先忽略細(xì)節(jié),開始畫豬,咱得先畫個(gè)豬腦袋吧,至少知道你畫的是個(gè)啥玩意兒,先整個(gè)效果都!

點(diǎn)拿到了,那就要開始拖了。那我們是不是得要知道兩個(gè)狀態(tài)啊,我到底是剛摁下去呢,還是在拖動(dòng),對(duì)吧,所以就有倆貨,UIGestureRecognizerStateBegan、UIGestureRecognizerStateChanged,一個(gè)是咱剛摁下去的狀態(tài),一個(gè)是在滑的狀態(tài),好了,這兩個(gè)狀態(tài)知道了,在UIGestureRecognizerStateBegan狀態(tài)是記錄開始的點(diǎn),在UIGestureRecognizerStateChanged狀態(tài)的時(shí)候記錄變化點(diǎn)。

開始動(dòng)用你的腦瓜子YY一下,咱拖的時(shí)候不是有一個(gè)痘痘一樣凸起的東西嗎?那咱先畫那個(gè)玩意兒。想想那個(gè)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;

第一個(gè)移到一個(gè)點(diǎn),這不難理解,既然要畫那不得知道從那開始,對(duì)吧!
第二個(gè)添加一條線,還是直的,想想那個(gè)曲線,明明是彎的好吧,所以pass掉
第三個(gè)畫的是彎的線,大致就是本來是直的線段,現(xiàn)在來了倆點(diǎn),然后按照切線的方向把線往一邊扯,然后彎了
第四個(gè)和第三個(gè)一樣,不過是兩個(gè)點(diǎn)變成了一個(gè)
第五個(gè),不管了,第六個(gè)把最后的起點(diǎn)和終點(diǎn)鏈接起來

方法的意思知道了,那就開始描線了
首先起點(diǎn)位置選擇,我們拿到拖動(dòng)的點(diǎn),那這個(gè)點(diǎn)的y肯定是線的中間位置(別問為什么,問就是只可意會(huì)不可言傳),線的中間點(diǎn)知道了,那就可以定一個(gè)起點(diǎn)和終點(diǎn)的y值,跨度多少呢,經(jīng)過不斷嘗試,大概200的跨度差不多,所以起點(diǎn)的y值就是changePoint.y - 100,終點(diǎn)就是changePoint + y。然后開始的x,既然從邊緣開始,那不就是屏幕的寬度嘛,so easy,所以起點(diǎn)的位置就是{Screen_Width, changePoint.y - 100},終點(diǎn)就是{Screen_Width, changePoint.y + 100}
所以

第二步

移動(dòng)到一個(gè)點(diǎn) (想啥呢,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)];

然后想想那個(gè)曲線,分解一下,第一段像是一個(gè)丿,第二段像是一個(gè)(,第三段像一個(gè)la(不好意思沒找到),那不就來了嘛,小老弟,直接上代碼(想屁吃呢,點(diǎn)還沒確定呢)

首先確定第一段終點(diǎn)位置,要丿到一個(gè)地方,首先y的位置肯定不能是拖動(dòng)點(diǎn)的y的位置,那玩意兒是中心點(diǎn),肯定比它要高,那就高個(gè)20吧(別問,問就是大概加估計(jì))。
再開始確定x的位置,首先比拖動(dòng)點(diǎn)的x要大(為什么大,從左到右,0~屏幕寬度,你說為啥大),然后大多少,憑借我這幾百度的近視眼估計(jì)了一下,大概是拖動(dòng)長(zhǎng)度的四分之一就夠了,那不就來了嘛,x就是tempPoint.x + 10* (Gesture_SCREEN_WIDTH - tempPoint.x) / 40,(有沒有不知道的,不知道算了),第一段終點(diǎn)位置就確定了{tempPoint.x + 10 * (Gesture_SCREEN_WIDTH - tempPoint.x) / 40, tempPoint.y - 20}tempPoint也就是拖動(dòng)點(diǎn),不過做了一些處理)

終點(diǎn)確定了,那就開始確定往哪個(gè)點(diǎn)彎,經(jīng)過不斷試錯(cuò)我來確定了一個(gè)(Gesture_SCREEN_WIDTH, tempPoint.y - 70),不多解釋,解釋就是憑經(jīng)驗(yàn)(錯(cuò)誤的經(jīng)驗(yàn)),所以代碼就是


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

第二段就簡(jiǎn)單了,對(duì)稱一下,就拿到終點(diǎn){tempPoint.x + 10 * (Gesture_SCREEN_WIDTH - tempPoint.x) / 40, tempPoint.y + 20},x不變,起點(diǎn)y20,終點(diǎn)就加20,控制點(diǎn)根據(jù)第一個(gè)點(diǎn)的方法判斷x就是tempPoint.x - 10 * (Gesture_SCREEN_WIDTH - tempPoint.x) / 40y自然就是中心點(diǎn)了,控制點(diǎn)就是{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];

第三步:

背景畫好了,那不得再來個(gè)圖片點(diǎn)綴一下?
再來個(gè)圖片的layer,添加到剛剛的layer
至于位置嘛,不就跟痘痘里的東西一樣嘛,對(duì)吧,所以直接就是{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);

至此,背景大功告成

第四步:

開始添磚加瓦
拖動(dòng)的時(shí)候,不能無限拖唄
所以得限制一下,如果拖動(dòng)到某個(gè)邊界的時(shí)候,繼續(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有沒有呢,當(dāng)然也有。當(dāng)我們?cè)谄聊豁敳炕虻撞康臅r(shí)候,起點(diǎn)和終點(diǎn)是不是就可能會(huì)超出屏幕了,所以限制一下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);
 }

既然那個(gè)拖動(dòng)展示的曲線要根據(jù)我當(dāng)前的位置,那我是不是只要我接觸點(diǎn)改變,就得重新繪制一次
另外就是直接返回首頁(yè),當(dāng)我拖到一定位置時(shí),幾秒中不動(dòng)就應(yīng)該觸發(fā)返回首頁(yè)的操作,但是?。。〔粍?dòng)?,屏幕感知那么靈敏,呼吸一下都有細(xì)微差距,所以不動(dòng),指的是宏觀上的不動(dòng),不要在意細(xì)小的差別,所以給個(gè)左右精度就好了,我這兒設(shè)置的是左右不超過3就算為不動(dòng)
所以思路有,那就簡(jiǎn)單了,上才藝,上代碼


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í)行類型以及圖標(biāo)
 */
- (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);
}

最后就是松手的時(shí)候了,當(dāng)手勢(shì)狀態(tài)為UIGestureRecognizerStateEnded,即是松手了。
那不得簡(jiǎn)簡(jiǎn)單單,直接調(diào)用返回不就完了,想屁吃呢,那個(gè)痘痘不是還展示在屏幕嗎?不打算給摁下去?
所以下一步操作來了,讓這個(gè)痘痘圓潤(rùn)的滾回去
所以咱怎么拖出來的,怎么給收回去不就好了嘛!
但是?。。。?!我不想搞了,你們玩兒吧,我直接簡(jiǎn)單粗暴一點(diǎn),給個(gè)動(dòng)畫,直接他移出去,雖然效果沒有重新繪制好,但是誰叫我懶呢。上代碼


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è)置默認(rèn)返回
    //代理的返回值控制是否自動(dòng)調(diào)用返回
    manager.delegate = self;
    //設(shè)置返回首頁(yè)圖片
    manager.config.returnHomeImageName = @"icon_pop_home";
    //config傳nil,就使用默認(rèn)的配置,也可以自各兒創(chuàng)建
    //頁(yè)面可單獨(dú)修改manager.config的各個(gè)配置
    //根控制器默認(rèn)不觸發(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;

/// 注冊(cè)
/// @param config 配置信息
/// @param block 注冊(cè)回調(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;
}


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

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

/**
 添加手勢(shì)到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, @"拖動(dòng)時(shí)返回的圖片未設(shè)置(returnImageName未設(shè)置)");
        }
    }if (self.config.isCanPopToRootViewController && self.config.returnHomeImageName.length == 0) {
        if (block) {
            block(YES, @"可以返回主頁(yè)時(shí)(isCanPopToRootViewController = YES),拖動(dòng)展示的圖片未設(shè)置(returnHomeImageName未設(shè)置)");
        }
    }else{
        if (block) {
            block(YES, @"當(dāng)前controller為導(dǎo)航控制器的根控制器時(shí),拖動(dòng)無效果");
        }
    }
    
#if DEBUG
    PPLog(@"==========Manager注冊(cè)信息==========\n");
    PPLog(@"NavigationController:%@\n",self.config.navigationController);
    PPLog(@"拖動(dòng)時(shí)的背景顏色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(@"拖動(dòng)時(shí)展示的圖片:%@\n",self.config.returnImageName);
    if (self.config.imageColor) {
        PPLog(@"拖動(dòng)時(shí)圖片的顏色: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(@"不改變拖動(dòng)時(shí)圖片的顏色\n");
    }
    PPLog(@"是否跟隨手勢(shì)位置移動(dòng):%@\n",self.config.isFollowGesturePosition ? @"是" : @"否");
    PPLog(@"是否可以返回首頁(yè):%@\n",self.config.isCanPopToRootViewController ? @"是" : @"否");
    PPLog(@"返回首頁(yè)拖動(dòng)時(shí)展示的圖片:%@\n",self.config.returnImageName);
    PPLog(@"返回首頁(yè)拖動(dòng)觸發(fā)時(shí)間:%g\n",self.config.returnHomeTime);
    PPLog(@"是否展示返回動(dòng)畫:%@\n",self.config.isShowPopAnimated ? @"是" : @"否");
#endif
}

/**
 屏幕邊界手勢(shì)執(zhí)行事件
 
 @param gesture 手勢(shì)
 */
- (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 滑動(dòng)點(diǎn)
 */
- (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í)行類型以及圖標(biāo)
 */
- (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)航控制器
 默認(rèn)為當(dāng)前window的導(dǎo)航控制器
 */
@property (nonatomic, strong, nullable) UINavigationController *navigationController;

/**
 拖動(dòng)時(shí),背景顏色
 默認(rèn)為0x999999
 */
@property (nonatomic, strong, nullable) UIColor *backGroundColor;

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

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

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

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

/**
 是否跟隨手勢(shì)位置移動(dòng)
 默認(rèn)為YES
 */
@property (nonatomic, assign) BOOL isFollowGesturePosition;

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


/**
 返回首頁(yè)拖動(dòng)時(shí)展示的圖片
 isCanPopToRootViewController為YES時(shí),必填
 */
@property (nonatomic, copy, nullable) NSString *returnHomeImageName;

/**
 返回首頁(yè)用時(shí)
 默認(rèn)為1秒
 */
@property (nonatomic, assign) CGFloat returnHomeTime;

/**
 是否執(zhí)行返回動(dòng)畫
 默認(rèn)為: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
/// 返回時(shí)代理
/// 重寫返回按鈕時(shí),可實(shí)現(xiàn)此方法處理數(shù)據(jù)
/// @param lastController 上一個(gè)controller
/// @param currentController 當(dāng)前controller
/// @return 是否自動(dòng)返回上一個(gè)controller
- (BOOL)CM_GestureRecognizerPopLastController:(UIViewController *)lastController currentController:(UIViewController *)currentController;

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

@end

NS_ASSUME_NONNULL_END

至此,搞定收工

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容