导航控制器的全屏返回手势[截图]的实现思路

导航控制器默认自带了侧滑pop掉当前控制器的功能,但是只有在界面的左边拖动的时候才会触发侧滑POP的功能,也就是说手势触发的范围只能在左边的一小块,说明系统手势触发的方法实现了滑动返回功能。

我们创建手势对象的时候,需要绑定监听者

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

当用户在界面左边滑动,有滑动返回功能,这是因为触发手势了,调用target的action方法,说明方法内部实现滑动返回功能,否则就不会有滑动返回效果。
在导航控制器的ViewDidLoad方法中打印滑动手势

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"%@",self.interactivePopGestureRecognizer);
    NSLog(@"%@",self.interactivePopGestureRecognizer.delegate); 
}

打印结果如下

 <UIScreenEdgePanGestureRecognizer: 0x7f87b16a9360; state = Possible; delaysTouchesBegan = YES; view = <UILayoutContainerView 0x7f87b16a7ac0>; target= <(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7f87b16aeb70>)>>
self.interactivePopGestureRecognizer.delegate =<_UINavigationInteractiveTransition: 0x7faf4a4356f0>

可以看出:
系统自带的手势是UIScreenEdgePanGestureRecognizer类型的对象 看名称就知道,这个手势的范围只能在屏幕的边缘
系统自带的手势的target 是_UINavigationInteractiveTransition类型的对象
系统调用的方法是handleNavigationTransition:
系统UIScreenEdgePanGestureRecognizer手势的代理就是_UINavigationInteractiveTransition

那么如何给自己的导航控制器添加1个全屏幕的滑动手势呢:
这里仅仅是提供一种思路 使用截图的方式来实现全屏滑动返回

为了方便演示 在工程中创建了1个有三个自控制器的导航控制器

basic.gif

在导航控制器的.h文件中
定义了一些宏

#import "GZDNavgationController.h"
#define kAnimatationDuration 0.25
#define kScreenWidth [UIScreen mainScreen].bounds.size.width
#define kScreenHeight [UIScreen mainScreen].bounds.size.height
#define kScreenSize [UIScreen mainScreen].bounds.size
#define kScreenBounds [UIScreen mainScreen].bounds

声明了三个属性

@interface GZDNavgationController ()
//装有所有截图的数组
@property (strong,nonatomic) NSMutableArray *screenImages;
//最后1个截图图片的imageView
@property (strong,nonatomic) UIImageView *lastImageView;
//遮罩
@property (strong,nonatomic) UIView *coverView;
@end

首先进行懒加载

-(NSMutableArray *)screenImages {
    if (!_screenImages) {
        _screenImages = [NSMutableArray array];
    }
    return _screenImages;
    
}

//为了保证不创建太多的imageview,所以使用懒加载的方式
- (UIImageView *)lastImageView{
    if (!_lastImageView) {
        _lastImageView = [[UIImageView alloc] init];
    }
    return _lastImageView;
}
//遮罩的view
- (UIView *)coverView{
    if (!_coverView) {
        _coverView = [[UIView alloc] init];
        _coverView.backgroundColor = [UIColor blackColor];
        _coverView.alpha = 0.5;
        _coverView.frame = kScreenBounds;
    }
    return _coverView;
}

在viewDidLoad 方法中创建平移手势

- (void)viewDidLoad {
    [super viewDidLoad];
   
    //创建平移手势
    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    
    //将平移手势添加到导航控制器上
    [self.view addGestureRecognizer:panGesture];
    
}

写了1个方法用来截取控制器全屏的图片

- (void)getScreenImage{
    //截取上一个控制器的全屏的图片
    
    //开启上下文
    UIGraphicsBeginImageContextWithOptions(kScreenSize, YES, 0.0);
    //将self.View渲染到图形上下文
    [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
    
    //从图形上下文中获得图片
    UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();
    //关闭图形上下文
    UIGraphicsEndImageContext();
    
    [self.screenImages addObject:screenShot];
    
}

由于第一张绿色控制器的的图片要在控制器显示的时候就截图下来,所以考虑在viewDidAppear 中截图添加到数组中

- (void)viewDidAppear:(BOOL)animated{
    
    [super viewDidAppear:animated];
    //因为导航控制器有可能会消失,出现多次
    //第一张图片只在数组没有元素的时候才截取,如果截图数组中有图片就不截取
    if (self.screenImages.count > 0)return;
    [self getScreenImage];
    
}

处理平移手势的方法

- (void)handlePanGesture:(UIPanGestureRecognizer *)pan{
    //如果子控制器的个数小于等于1-->就是导航控制器里面只有根控制器在,此时是不需要手势,直接返回
    if (self.viewControllers.count <= 1)return;
    //手指在屏幕上水平方向移动的距离
    CGFloat moveX = [pan translationInView:self.view].x;
//如果手指向左移动此时moveX <0 ,无需判断左移的情况 所以直接返回.
    if (moveX < 0)return;
//能来到下面 说明满足手势触发的条件 即不是导航控制器的根控制器 同时手势是向右边滑动
    //改变导航控制器的view的transform,使整个控制器都能够向左移动
    self.view.transform = CGAffineTransformMakeTranslation(moveX, 0);
    //将导航控制器的view平移操作之后下面是没有任何东西的,所以会显示为黑色.
//考虑将截图的view 添加到window 上面
    //获取window对象
    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    //取出数组中的最后1个image将其加载到懒加载的imageView中
    self.lastImageView.image
    = self.screenImages[self.screenImages.count - 2];
//设置frame
    self.lastImageView.frame = kScreenBounds;
    //在window上插入这个view
    [window insertSubview:self.lastImageView atIndex:0];
    //添加遮罩view  将遮罩的view 插入到lastImageView 的上面
    [window insertSubview:self.coverView aboveSubview:self
     .lastImageView];
    //拿到self的view 的x
    CGFloat x = self.view.frame.origin.x;
    //如果手势的状态是停止的时候
    //判断最终的停靠位置
    if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) {
        //如果平移的距离比屏幕宽度的一半还要大
        if (x >= kScreenWidth * 0.5){
            [UIView animateWithDuration:kAnimatationDuration animations:^{
                self.view.transform = CGAffineTransformMakeTranslation(kScreenWidth, 0);
            }completion:^(BOOL finished) {
          //完成平移之后控制器出栈
                [self popViewControllerAnimated:NO];
                //导航控制器的view 被移动到最右边去了
                self.view.transform = CGAffineTransformIdentity;
            }];
            
        }else{//如果平移的距离比屏幕宽度的一半要小
            [UIView animateWithDuration:kAnimatationDuration animations:^{
                //还原控制器
                self.view.transform = CGAffineTransformIdentity;
            }];
        }
    }
    
}


重写两个方法 拦截push 和pop的操作

//拦截push操作的方法 在这个方法里面截图
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
 //在调用super使控制器入栈之后
    [super pushViewController:viewController animated:animated];
//截图
    [self getScreenImage];
}
//拦截pop操作的方法
- (UIViewController *)popViewControllerAnimated:(BOOL)animated{
//移除图片
    [self.lastImageView removeFromSuperview];
//移除遮罩
    [self.coverView removeFromSuperview];
//移除截图数组中的最后1个元素
    [self.screenImages removeLastObject];
//调用父类pop方法出栈
    return [super popViewControllerAnimated:animated];
}

最后实现的效果

完成.gif

可以看到在滑动栈顶控制器的时候 其实显示在下面的是1个全屏幕的截图而已...

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

推荐阅读更多精彩内容