IOS手势滑动返回总结(边缘+全屏)

为了提高用户体验,在controller会加上这个操作,我自己写了好多次,但是没有系统的整理过,这会儿又做到这个功能了,索性整理一下。

一、边缘滑动返回

在远古时代,大概是ios7之前,滑动返回这个事儿是不被官方支持的,因为手机屏幕没那么大,IOS7以后,苹果为了提升用户体验,增加了【边缘返回】的手势,注意是边缘,不是全屏,并且在特定条件下,边缘返回会失效,具体是以下几种情况:

1. 自定义了navigationItem的leftBarButtonItem或leftBarButtonItems

2. self.navigationItem.hidesBackButton = YES

3. self.navigationItem.leftItemsSupplementBackButton = NO


为了解决以上问题,有两个方案,拿捏:


方案一:

在UINavigationController基类添加以下:

- (void)viewDidLoad

{

    [super viewDidLoad];

    //设置右滑返回手势的代理为自身

    __weak typeof(self) weakself = self;

    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {

        self.interactivePopGestureRecognizer.delegate = (id)weakself;

    }

}

#pragma mark - UIGestureRecognizerDelegate

//这个方法是在手势将要激活前调用:返回YES允许右滑手势的激活,返回NO不允许右滑手势的激活

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

{

    if (gestureRecognizer == self.interactivePopGestureRecognizer) {

        //屏蔽调用rootViewController的滑动返回手势,避免右滑返回手势引起crash

        if (self.viewControllers.count < 2 ||

self.visibleViewController == [self.viewControllers objectAtIndex:0]) {

            return NO;

        }

    }

    //这里就是非右滑手势调用的方法啦,统一允许激活

    return YES;

}


那么,在特定场景下,我们不希望用户轻易返回,比如在直播间内、在扫码界面等,拿捏:


创建一个UIViewController 的分类:

+ (void)popGestureClose:(UIViewController *)VC

{

    // 禁用侧滑返回手势

    if ([VC.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {

        //这里对添加到右滑视图上的所有手势禁用

        for (UIGestureRecognizer *popGesture in VC.navigationController.interactivePopGestureRecognizer.view.gestureRecognizers) {

            popGesture.enabled = NO;

        }

        //若开启全屏右滑,不能再使用下面方法,请对数组进行处理

        //VC.navigationController.interactivePopGestureRecognizer.enabled = NO;

    }

}

+ (void)popGestureOpen:(UIViewController *)VC

{

    // 启用侧滑返回手势

    if ([VC.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {

    //这里对添加到右滑视图上的所有手势启用

        for (UIGestureRecognizer *popGesture in VC.navigationController.interactivePopGestureRecognizer.view.gestureRecognizers) {

            popGesture.enabled = YES;

        }

        //若开启全屏右滑,不能再使用下面方法,请对数组进行处理

        //VC.navigationController.interactivePopGestureRecognizer.enabled = YES;

    }

}


使用:

- (void)viewDidAppear:(BOOL)animated

{

    [super viewDidAppear:animated];

    [UIViewController popGestureClose:self]; //关闭边缘返回

}

- (void)viewWillDisappear:(BOOL)animated

{

    [super viewWillDisappear:animated];

    [UIViewController popGestureOpen:self]; //启动边缘返回

}


方案二:

每个UIViewController都有一个backBarButtonItem,这是个特殊属性,只响应页面的返回和销毁,表现为:只能自定义image和title,不能重写target 或 action。(注意:UINavigationController的左侧是不支持右滑返回手势的)我们通过自定义backBarButtonItem,来实现:既实现“自定义返回按钮(通常自定义leftBarButtonItem或leftBarButtonItems都是为了实现自定义返回按钮)”又保留滑动返回。

拿捏:

在UIViewController基类:

- (void)viewDidLoad{


    [super viewDidLoad];

UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];

    //自定义返回按钮的视图

    [self.navigationController.navigationBar setBackIndicatorImage:[UIImage imageNamed:@"navi_back_icon"]];

    [self.navigationController.navigationBar setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"navi_back_icon"]];

    //设置tintColor 改变自定图片颜色

    self.navigationController.navigationBar.tintColor = [UIColor whiteColor];

    //设置自定义的返回按钮

    self.navigationItem.backBarButtonItem = backItem;

}

那么在这种方案下,在特定场景我们不希望用户轻易返回,如何做?

拿捏:

自定义`leftBarButtonItem`或`leftBarButtonItems`,并设置`leftItemsSupplementBackButton = YES`。

- (void)viewDidLoad{


    [super viewDidLoad];

    //自定义返回按钮

    UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];

    [studySearch setImage:[UIImage imageNamed:@"back_icon"] forState:UIControlStateNormal];

    [studySearch sizeToFit];

    [studySearch addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];

    UIBarButtonItem *studySearchItem = [[UIBarButtonItem alloc] initWithCustomView:studySearch];

    self.navigationItem.leftBarButtonItems = @[studySearchItem];

    //是否支持显示左滑返回按钮,

    //NO不显示:leftBarButtonItems覆盖backBarButtonItem,

    //YES显示:backBarButtonItem 显示在leftBarButtonItems左侧

    //leftItemsSupplementBackButton必须在自定义leftBarButtonItem或leftBarButtonItems后才有效

    self.navigationItem.leftItemsSupplementBackButton = YES;

}

以上两个方案已经可以满足大部分开发需求,但还有一种情况,在UIScrollView(UICollectionView)下,返回手势会失灵。

我们先来看看啥原理:

UIScrollView(包括其子类UITextView、UITableView、UICollectionView等)的panGestureRecognizer先接收到手势事件,处理后不再往下传递。即是否让两个panGestureRecognizer都起作用的问题,默认情况下scrollView的手势会让系统的手势失效。so,显而易见,我们需要让两个手势同时启用。

拿捏:

创建UIScrollView的分类

@implementation UIScrollView (PopGesture)

//此方法返回YES时,手势事件会一直往下传递,不论当前层次是否对该事件进行响应。

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{

    return YES;

}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{

    return YES;

}

@end



二、全屏滑动返回

全屏返回这种骚功能,官方从未提供过,可爱的程序员们自己搞出来,以前的做法(ios7之前)大概就是“手势+截图”,画风是这样的:

- (void)viewDidLoad{

    [super viewDidLoad];

    UIImageView *shadowImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"leftside_shadow_bg"]];

    shadowImageView.frame = CGRectMake(-10, 0, 10, self.view.frame.size.height);

    [self.view addSubview:shadowImageView];


    UIPanGestureRecognizer *recognizer = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(paningGestureReceive:)];

    [recognizer setDelegate:self];

    [recognizer delaysTouchesBegan];

    [self.view addGestureRecognizer:recognizer];

}


- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{

    if (self.viewControllers.count > 0) {

        [self.screenShotsList addObject:[self capture]]; //截图,并放入数组

    }

    [super pushViewController:viewController animated:animated];

}

- (nullable NSArray<__kindof UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated {

    [self.screenShotsList removeAllObjects]; //清空截图

    return [super popToRootViewControllerAnimated:animated];

}

- 感兴趣的同学可以在 这里 看到完整代码 -


自从ios7支持【边缘滑动】返回后,【全屏返回】的实现又多了一种思路,江湖人称:移花接木。同样有两个方案。


拿捏:


方案一:

在UINavigationController基类中:


- (void)viewDidLoad{

    [super viewDidLoad];

    // 获取系统自带滑动手势的target对象

    id target = self.interactivePopGestureRecognizer.delegate;

    // 创建全屏滑动手势,调用系统自带滑动手势的target的action方法

    SEL handler = NSSelectorFromString(@"handleNavigationTransition:");

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:handler];

//设置手势代理,拦截手势触发

pan.delegate = self;

//添加全屏滑动手势

[self.interactivePopGestureRecognizer.view addGestureRecognizer:pan];

// 禁止使用系统自带的边缘滑动手势

self.interactivePopGestureRecognizer.enabled = NO;

    //设置右滑返回手势的代理为自身

    __weak typeof(self) weakself = self;

    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {

        self.interactivePopGestureRecognizer.delegate = (id)weakself;

    }

}

注意,这里的  pan.delegate = self;  可能系统会打警告⚠️,因为没有申明和实现代理  UIGestureRecognizerDelegate  ,不实现也木有关系的,不过实现的话,可以再加一些判断,出于安全和为了去掉警告,我们来加一下。

拿捏:

@interface UIViewController()<UIGestureRecognizerDelegate>

@end

... ...

- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer{

    //控制器栈里只有一个,不响应

    if (self.navigationController.viewControllers.count <= 1) {

        return NO;

    }

    // 当控制器正在返回的时候,不响应

    if ([[self.navigationController valueForKey:@"_isTransitioning"] boolValue]) {

        return NO;

    }

    //只能响应 从左到右的滑动

    CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view];

    if (translation.x <= 0) {

        return NO;

    }

    return YES;

}

还有一处细节,每个UIViewController都会默认添加 navigationController.interactivePopGestureRecognizer手势,而我们再基类又给加了一次,这不是变成两个interactivePopGestureRecognizer了吗,既然如此,我们禁用掉一个!

拿捏:

在UIViewController基类中:

- (void)viewDidLoad{

    [super viewDidLoad];

  if(self.navigationController.interactivePopGestureRecognizer.view.gestureRecognizers.count == 2 ){

        for (UIGestureRecognizer *popGesture in self.navigationController.interactivePopGestureRecognizer.view.gestureRecognizers) {

            popGesture.enabled = NO;

            break;

        }

    }

}


方案二

此方案最方便快捷。

pod 'FDFullscreenPopGesture'

pod 'TZScrollViewPopGesture'

- FDFullscreenPopGesture 为每个UIViewController添加【全屏滑动返回】,但遇到UIScrollView就无效了

- TZScrollViewPopGesture 主要是实现【边缘滑动返回】功能,不过这个是次要,主要是,它提供了给UIScrollView添加【边缘滑动返回】的功能。

这俩不会互相影响,功能互补,详细原理可以去看看源码,这儿就不细写了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容