首先iOS7以后系统默认自带了侧滑功能,当用户在界面的左边滑动的时候,就会有侧滑功能。 但是如果我们从从导航控制器的返回按钮,就发现系统所带的侧滑返回功能无法使用,而且有些功能不尽人意.系统自定义的优点在于,当界面中有其它易冲突手势(像某控制器界面本身的轻扫或左滑右滑手势)时,系统滑动方法是边缘手势,与其它手势的作用区域可能会有不同,会有益于解决这些冲突。

所以有以下自定义方法。

1.全屏手势滑动

- (void)viewDidLoad {[super viewDidLoad];// 获取系统自带滑动手势的target对象id target = self.interactivePopGestureRecognizer.delegate;// 创建全屏滑动手势,调用系统自带滑动手势的target的action方法UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:@selector(handleNavigationTransition:)];// 设置手势代理,拦截手势触发pan.delegate = self;// 给导航控制器的view添加全屏滑动手势[self.view addGestureRecognizer:pan];// 禁止使用系统自带的滑动手势self.interactivePopGestureRecognizer.enabled = NO;
}// 什么时候调用:每次触发手势之前都会询问下代理,是否触发。
// 作用:拦截手势触发
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{// 注意:只有非根控制器才有滑动返回功能,根控制器没有。// 判断导航控制器是否只有一个子控制器,如果只有一个子控制器,肯定是根控制器if (self.childViewControllers.count == 1) {// 表示用户在根控制器界面,就不需要触发滑动手势,return NO;}return YES;
}

该方法实现了全屏手势滑动,但是,当当前视图有其它手势时可能产生冲突。

2.部分视图没有右滑返回

@property (nonatomic,strong) UIViewController *currentShowVC;

-(void)viewWillAppear:(BOOL)animated
{
    self.navigationController.navigationBarHidden=YES;
    [_firstVC hidenLabel];
    
    
    //设置代理
    self.navigationController.interactivePopGestureRecognizer.delegate =(id)self;
    
    //启用系统自带的滑动手势
    self.navigationController.interactivePopGestureRecognizer.enabled = YES;

//判断当在视图栈的第几个是不用手势返回,一般为1,我这里因为有一个空白的跟视图所以设置为2

if (self.navigationController.viewControllers.count == 2){
        //将当前导航控制器置空
        self.currentShowVC = Nil; 
    }else{
        
        self.currentShowVC = self;
        
    }

}

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    
    if (gestureRecognizer == self.navigationController.interactivePopGestureRecognizer) {
        //当前导航控制器是根视图控制器
        //the most important
        return (self.currentShowVC == self.navigationController.topViewController);
       
        
    }
    
    return YES;
    
}

关于部分界面没有返回手势

1.在当前界面的

-(void)viewDidAppear:(BOOL)animated
{
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}

2.在下一个需要用到返回手势的界面

-(void)viewWillAppear:(BOOL)animated

{

//设置代理

self.navigationController.interactivePopGestureRecognizer.delegate =(id)self;

//启用系统自带的滑动手势

self.navigationController.interactivePopGestureRecognizer.enabled = YES;

}

3.自定义返回动画

.h文件

#import <UIKit/UIKit.h>

@interface AnimatedNavigationController : UINavigationController

@end

.m文件

#import "AnimatedNavigationController.h"

@interface AnimatedNavigationController ()

@end

#import "ViewController.h"
@interface AnimatedNavigationController ()
{
    // 屏幕截图
    UIImageView *_screenshotImgView;
    // 截图上面的黑色半透明遮罩
    UIView *_coverView;
    
    // 存放所有截图
    NSMutableArray *_screenshotImgs;
}

@end

@implementation AnimatedNavigationController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // 1,创建Pan手势识别器,并绑定监听方法
    UIPanGestureRecognizer *panGestureRec = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panGestureRec:)];
    // 为导航控制器的view添加Pan手势识别器
    [self.view addGestureRecognizer:panGestureRec];
    
    
    
    // 2.创建截图的ImageView
    _screenshotImgView = [[UIImageView alloc] init];
    // app的frame是除去了状态栏高度的frame
    _screenshotImgView.frame = [UIScreen mainScreen].applicationFrame;
    //(0 20; 320 460);
    
    // 3.创建截图上面的黑色半透明遮罩
    _coverView = [[UIView alloc] init];
    // 遮罩的frame就是截图的frame
    _coverView.frame = _screenshotImgView.frame;
    // 遮罩为黑色
    _coverView.backgroundColor = [UIColor blackColor];
    
    // 4.存放所有的截图数组初始化
    _screenshotImgs = [NSMutableArray array];
    
    
    
}

// 重写push方法,在push之前 先截取图片
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    // 只有在导航控制器里面有子控制器的时候才需要截图
    if (self.viewControllers.count >= 1) {
        // 调用自定义方法,使用上下文截图
        [self screenShot];
    }
    // 截图完毕之后,才调用父类的push方法
    [super pushViewController:viewController animated:YES];
}

// 使用上下文截图,并使用指定的区域裁剪,模板代码
- (void)screenShot
{
    // 将要被截图的view,即窗口的根控制器的view(必须不含状态栏,默认ios7中控制器是包含了状态栏的)
    UIViewController *beyondVC = (UIViewController *)self.view.window.rootViewController;
    // 背景图片 总的大小
    CGSize size = beyondVC.view.frame.size;
    // 开启上下文,使用参数之后,截出来的是原图(YES  0.0 质量高)
    UIGraphicsBeginImageContextWithOptions(size, YES, 0.0);
    // 要裁剪的矩形范围
    CGRect rect = CGRectMake(0, -20.8, size.width, size.height + 20 );
    //注:iOS7以后renderInContext:由drawViewHierarchyInRect:afterScreenUpdates:替代
    [beyondVC.view drawViewHierarchyInRect:rect  afterScreenUpdates:NO];
    // 从上下文中,取出UIImage
    UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
    // 添加截取好的图片到图片数组
    [_screenshotImgs addObject:snapshot];
    
    // 千万记得,结束上下文(移除栈顶的基于当前位图的图形上下文)
    UIGraphicsEndImageContext();
    
}

// 监听手势的方法,只要是有手势就会执行
- (void)panGestureRec:(UIPanGestureRecognizer *)panGestureRec
{
    
    // 如果当前显示的控制器已经是根控制器了,不需要做任何切换动画,直接返回
    if(self.topViewController == self.viewControllers[0]) return;
    // 判断pan手势的各个阶段
    switch (panGestureRec.state) {
        case UIGestureRecognizerStateBegan:
            // 开始拖拽阶段
            [self dragBegin];
            break;
            
        case UIGestureRecognizerStateEnded:
            // 结束拖拽阶段
            [self dragEnd];
            break;
            
        default:
            // 正在拖拽阶段
            [self dragging:panGestureRec];
            break;
    }
}

#pragma mark 开始拖动,添加图片和遮罩
- (void)dragBegin
{
    // 重点,每次开始Pan手势时,都要添加截图imageview 和 遮盖cover到window中
    [self.view.window insertSubview:_screenshotImgView atIndex:0];
    [self.view.window insertSubview:_coverView aboveSubview:_screenshotImgView];
    
    // 并且,让imgView显示截图数组中的最后(最新)一张截图
    _screenshotImgView.image = [_screenshotImgs lastObject];
}

// 默认的将要进行缩放的截图的初始比例
#define kDefaultScale 0.6
// 默认的将要变透明的遮罩的初始透明度(全黑)
#define kDefaultAlpha 1.0

// 当拖动的距离,占了屏幕的总宽高的3/4时, 就让imageview完全显示,遮盖完全消失
#define kTargetTranslateScale 0.75
#pragma mark 正在拖动,动画效果的精髓,进行缩放和透明度变化
- (void)dragging:(UIPanGestureRecognizer *)pan
{
    
    // 得到手指拖动的位移
    CGFloat offsetX = [pan translationInView:self.view].x;
    // 只允许往右边拖,禁止向左拖
    if (offsetX < 0) offsetX = 0;
    // 让整个view都平移     // 挪动整个导航view
    self.view.transform = CGAffineTransformMakeTranslation(offsetX, 0);
    
    // 计算目前手指拖动位移占屏幕总的宽高的比例,当这个比例达到3/4时, 就让imageview完全显示,遮盖完全消失
    double currentTranslateScaleX = offsetX/self.view.frame.size.width;
    
    // 让imageview缩放,默认的比例+(当前平衡比例/目标平衡比例)*(1-默认的比例)
    double scale = kDefaultScale + (currentTranslateScaleX/kTargetTranslateScale) * (1 - kDefaultScale);
    // 已经达到原始大小了,就可以了,不用放得更大了
    if (scale > 1) scale = 1;
    _screenshotImgView.transform = CGAffineTransformMakeScale(scale, scale);
    
    // 让遮盖透明度改变,直到减为0,让遮罩完全透明,默认的比例-(当前平衡比例/目标平衡比例)*默认的比例
    double alpha = kDefaultAlpha - (currentTranslateScaleX/kTargetTranslateScale) * kDefaultAlpha;
    _coverView.alpha = alpha;
}

#pragma mark 结束拖动,判断结束时拖动的距离作相应的处理,并将图片和遮罩从父控件上移除
- (void)dragEnd
{
    // 取出挪动的距离
    CGFloat translateX = self.view.transform.tx;
    // 取出宽度
    CGFloat width = self.view.frame.size.width;
    
    if (translateX <= width * 0.5) {
        // 如果手指移动的距离还不到屏幕的一半,往左边挪 (弹回)
        [UIView animateWithDuration:0.3 animations:^{
            // 重要~~让被右移的view弹回归位,只要清空transform即可办到
            self.view.transform = CGAffineTransformIdentity;
            // 让imageView大小恢复默认的scale
            _screenshotImgView.transform = CGAffineTransformMakeScale(kDefaultScale, kDefaultScale);
            // 让遮盖的透明度恢复默认的alpha 1.0
            _coverView.alpha = kDefaultAlpha;
        } completion:^(BOOL finished) {
            // 重要,动画完成之后,每次都要记得 移除两个view,下次开始拖动时,再添加进来
            [_screenshotImgView removeFromSuperview];
            [_coverView removeFromSuperview];
        }];
    } else {
        // 如果手指移动的距离还超过了屏幕的一半,往右边挪
        [UIView animateWithDuration:0.3 animations:^{
            // 让被右移的view完全挪到屏幕的最右边,结束之后,还要记得清空view的transform
            self.view.transform = CGAffineTransformMakeTranslation(width, 0);
            // 让imageView缩放置为1
            _screenshotImgView.transform = CGAffineTransformMakeScale(1, 1);
            // 让遮盖alpha变为0,变得完全透明
            _coverView.alpha = 0;
        } completion:^(BOOL finished) {
            // 重要~~让被右移的view完全挪到屏幕的最右边,结束之后,还要记得清空view的transform,不然下次再次开始drag时会出问题,因为view的transform没有归零
            self.view.transform = CGAffineTransformIdentity;
            // 移除两个view,下次开始拖动时,再加回来
            [_screenshotImgView removeFromSuperview];
            [_coverView removeFromSuperview];
            
            // 执行正常的Pop操作:移除栈顶控制器,让真正的前一个控制器成为导航控制器的栈顶控制器
            [self popViewControllerAnimated:NO];
            
            // 重要~记得这时候,可以移除截图数组里面最后一张没用的截图了
            [_screenshotImgs removeLastObject];
        }];
    }
}
@end

demo地址: https://github.com/hyf12138/NavigationDemo.git

iOS 右滑手势返回上一级相关推荐

  1. iphone11返回上一级手势怎么设置_苹果iphone12怎么关闭程序appp 怎么返回上一步

    苹果iphone12怎么关闭程序appp 怎么返回上一步 相信有很多朋友还不太熟悉iphone12系列的使用方法,今天小编就为大家带来了,iphone12怎么关闭程序,以及iphone12怎么返回上一 ...

  2. iphone11返回上一级手势怎么设置_iPhone11怎么返回上一级-使用教程

    近日iPhone11的销量又回升了,相信也有不少刚入手的用户,那么小编今天就为大家带来了iPhone11的使用教程详情,感兴趣的朋友不要错过了哦. iPhone11怎么返回上一级 方法一:我们需要在返 ...

  3. H5页面与ios交互返回上一级

    H5页面与ios交互时,返回上一级不兼容,安卓兼容 实现方法  (需要加return false) <a href="#" onclick="javascript: ...

  4. Android 手势返回上一界面 亲测可用

    在现如今,Android使用越来越便捷,也对Android开发提出更高要求,便捷体现在开发的每一处. 这里介绍Android使用手势返回上一界面.亲测可用! 一.思考: 1. 监听可手势返回上一界面的 ...

  5. 【转】iOS右滑返回手势全解和最佳实施方案

    序言 在ios7以后,苹果推出了手势滑动返回功能,也就是从屏幕左侧向右滑动可返回上一个界面.大大提高了APP在大屏手机和iPad上的操作体验,场景切换更加流畅.做右滑返回手势配置时,可能会遇到的 问题 ...

  6. 右滑手势导航返回的相关设置

    iOS7之后提供了右滑返回上一级界面的手势,但是自定义返回按钮会失效,解决办法如下: -(void)viewWillAppear:(BOOL)animated{ [superviewWillAppea ...

  7. iphone11返回上一级手势怎么设置_华为手机的这五种导航方式,你更习惯哪一种?怎么切换?...

    手机的导航方式是人机交互的基础功能,随着智能手机进入全面屏时代,手势导航成了大家常用的系统导航方式,但也有人还是习惯于其他几种导航方式,下面就让我们一起来解读这几种导航方式. 手势导航 先来说下当下流 ...

  8. popstate返回上一级问题。

    关于监听页面返回上一级popstate,进入页面后,安卓手机必须手指点击页面任何部分之后返回才会触发popstate,IOS正常触发. 代码如下: window.addEventListener(&q ...

  9. 实现苹果系统自带的侧滑返回上一级效果

    最近在公司老项目上更改,准备做版本迭代,发现此版本不能实现侧滑返回上一级效果, 最后在navigationController里面找到了突破口 @property(nullable, nonatomi ...

最新文章

  1. 为Spring Cloud Config Server配置远程git仓库
  2. 【数据结构】八大数据结构分类
  3. Object component的各种标识符
  4. 转-HTTPClient调用https请求,通过基本认证用户名密码(Basic Auth)
  5. 分页插件PageHelper的使用方法
  6. 深度学习面试的一些知识
  7. XML 与动态添加控件
  8. 计算机二级c语言填空题库,全国计算机二级C语言填空题库.doc
  9. sentinel——SLC数据下载、DEM数据下载拼接、精密轨道数据下载方法
  10. Linux卸载驱动方法
  11. HWP转Word说明
  12. 华硕怎么安装linux系统教程,有关华硕电脑无法安装Ubuntu系统的解决方案
  13. iOS App Security and Analysis: Part 1/2
  14. ASP.NET增加微信公众号功能
  15. 校园网服务器系统方案设计,校园网服务器系统项目设计方案.pdf
  16. 【Git学习】解决GitLab内存消耗大的问题
  17. SQL Server2005 只有配置工具,而没有查询分析器、企业管理器的解决方法
  18. 这个地方沸腾,高手争雄,至尊大决战,从天上杀到地下,又从地上打到云霄上!
  19. 11 RabbitMQ消息的可靠性保障
  20. Delphi-UpperCase 函数

热门文章

  1. SSRS 报表 日期类表达式
  2. PHP Yac cache 源码学习笔记
  3. k8s集成jenkins权限故障记录
  4. wordpress商城开发 线下paypal支付插件
  5. 在ns2.35下完成柯老师lab18实验
  6. PwC子公司Strategy:STO“与ICO没有根本的区别”
  7. 精通WordPress设计与开发:第1章 你的 第一篇WordPress帖子
  8. python提权_渗透利器 | 提权辅助工具箱
  9. 一个简单粗暴的营销方案,让麻辣烫老店业绩增长40倍以上!
  10. 香港夫妇道琼斯内幕交易狂赚800万解密