您的位置:首页 > 移动开发 > IOS开发

自定义全屏滑动返回手势

2016-12-26 18:47 141 查看
iOS中如果不自定义UINavigationBar,通过手势向右滑是可以实现返回的,这时左边的标题文字提示的是上一个ViewController的标题,如果需要把文字改为简约风格,例如弄过箭头返回啥的,那么你需要自定义UINavigationBar,但当你自定义navigationBar后,这个功能就会自动失效。
 
屏蔽右滑返回功能代码:
 

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

    self.navigationController.interactivePopGestureRecognizer.enabled = NO;    

}  

 
开启滑动返回功能代码:
 

- (void)viewWillAppear:(BOOL)animated{  

    [super viewWillAppear:animated];  

      

    // 右滑返回  

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

        self.navigationController.interactivePopGestureRecognizer.delegate = nil;  

    }  

  

}  

 
注意各种坑:
"在一级视图中,iOS样式返回的手势滑动一下,然后进入二级视图,发现画面卡住了,按Home键转入后台,再返回应用,发现并没有Crash掉,而是直接跳到了二级视图里,运行正常了,大家知道push和pop的原理是用进栈出栈完成的,可能因为在一级视图中滑动那一下,影响了视图在栈中的位置。 "
------有人提到通过以下方法处理:“一级视图中一定要加入self.navigationController.interactivePopGestureRecognizer.enabled = NO;,先把iOS7手势返回屏蔽掉,到二级视图再用self.navigationController.interactivePopGestureRecognizer.enabled = YES打开”
 
自己写了个demo试运行,发现self.navigationController.interactivePopGestureRecognizer.enabled 不能动态设置更改状态。因此该方法不可行。
 
解决方法:
 

- (void)viewDidAppear:(BOOL)animated  

{  

__weak typeof(self) weakSelf = self;  

 self.navigationController.interactivePopGestureRecognizer.delegate = weakSelf;  

}  

实现手势协议:
 
 

#pragma mark - UIGestureRecognizerDelegate  

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

    //判断是否为rootViewController  

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

        return NO;  

    }  

    return YES;  

}  

 
但问题又来了,如果是一个显示成功/失败结果页,滑动返回不大符合正常思维,因为需要选择性屏蔽处理。
终极解决方法:自定义全屏滑动手势UIPanGestureRecognizer
 

//  

//  BasicNavigationController.m  

//  

//  

//  Copyright (c) 2016年 lvxiangan520@126.com. All rights reserved.  

//  

  

#import "BasicNavigationController.h"  

#import "BaseResultViewController.h"  

  

@interface BasicNavigationController() <UIGestureRecognizerDelegate>  

  

@end  

  

@implementation BasicNavigationController  

  

- (void)viewDidLoad  

{  

    [super viewDidLoad];  

      

    [self.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName : WhiteColor}];  

      

    // 获取系统自带滑动手势的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;  

      

}  

  

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

{  

    [viewController.navigationItem.backBarButtonItem setTitleTextAttributes:@{NSFontAttributeName : [UIFont systemFontOfSize:Scale_Size_Smaller()]} forState:UIControlStateNormal];  

    if (self.childViewControllers.count > 0) {  

        viewController.hidesBottomBarWhenPushed = YES;  

    }  

    [super pushViewController:viewController animated:YES];  

}  

  

  

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer  

{  

    // 注意:只有非根控制器才有滑动返回功能,根控制器没有。  

    // 判断导航控制器是否只有一个子控制器,如果只有一个子控制器,肯定是根控制器  

    if (self.childViewControllers.count == 1) {  

        // 表示用户在根控制器界面,就不需要触发滑动手势,  

        return NO;  

    }  

    // 当前页面是显示结果页,不响应滑动手势  

    UIViewController *vc = [self.childViewControllers lastObject];  

    if ([vc isKindOfClass:[BaseResultViewController class]]) {  

        return NO;  

    }  

      

    return YES;  

}  

  

  

  

@end  

===============================


iOS自定义导航条(页面手势返回时 导航条更随控制器一起滑动)

 
 
前段时间被问到一个问题就是一个JD的UI效果如图

IMG_0485.PNG

 

其实就是一个原生的导航条 一个是自定义的导航条

第一想法 肯定是 将原生的navgationBar 隐藏掉 让后搞一个UIview 覆盖到原来的位置, 然后在UIview 的上面分别添加左button 和右button 和中间的titleview 当然这样做汪全是可以的 但是我感觉那么 navgationBar 使用的时候就不够方便了
所以 我们可以单独创建一个NavgationBar 然后添加到视图控制器上
那么首先我们创建一个基类 让其继承与UIviewcontroler 并且项目中的其他项目全部继承与这个基类
#import <UIKit/UIKit.h>
#import "ZJNavgationbar.h"
@interface ZJbaseViewController : UIViewController

@property (nonatomic,strong)UINavigationBar * NavigationBar;
@property (nonatomic,strong)UINavigationItem *ZJNavigationItem;

@end

将UINavigationBar定义成基类控制器的公开属性
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];

if (!self.NavigationBar) {
self.NavigationBar = [[UINavigationBar alloc]initWithFrame:CGRectMake(0,0,self.view.frame.size.width,64)];
[self.view addSubview:self.NavigationBar];
self.ZJNavigationItem = [[UINavigationItem alloc]initWithTitle:@"hellows"];
[self.NavigationBar pushNavigationItem:self.ZJNavigationItem animated:YES];
UIBarButtonItem * logeItem = [[UIBarButtonItem alloc]initWithTitle:@"登录" style:UIBarButtonItemStylePlain target:self action:@selector(logeClick)];
NSArray * arr = @[logeItem];
self.ZJNavigationItem.rightBarButtonItems = arr;
if ([self.navigationController.childViewControllers count]>1) {

UIBarButtonItem * backItem = [[UIBarButtonItem alloc]initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:@selector(backclick)];
NSArray * arrleft = @[backItem];
self.ZJNavigationItem.leftBarButtonItems = arrleft;
}
}

}

在控制器的 viewWillAppear 中判断是否存在 NavgationBar 存在的话就不创建 然则反之.

并且把 navgationbar 添加在 控制器的self.view 上

那么 说有集成于基类的控制器 都会拥有这个导航栏
2.导航栏添加名称

首先分析 原生的导航栏添加标题
self.navigationItem.title = @"名称";

我们可以看出来 标题时添加在navgationitem 上 而且 navgationitem 是控制器的属性 而且是只读的 这时候在通过这个属性赋值的话 就会失效
那么为了方便 我们应该同事也自定义一个UINavigationItem

同时也将 属性暴漏出来 如上述代码

这时候呢 那么 添加左右button 也就可以和原来一样 而且可以实现上图的效果.

但是还有一点问题就是 如果控制器是带xib 文件的话 那么导航栏的宽度会比控制器款

F9B1A4A9-0D36-455A-88F0-D29EF472AAB5.png

正常的控制器效果是这样的

6495EAEE-55FD-47AB-B5C7-A29CE41795BF.png

大概原因因该是我现在了 viewwillapper 中的缘故

有啥好的解决办法 可以告诉我哦
以后还会更加深入的解析
感觉好像忘了点啥 那就是 让界面允许全屏滑动返回

很简单只要一句代码搞定

在基类的viewvillappear中添加这一句话 这是kvc 的方法 
[self.navigationController.interactivePopGestureRecognizer setValue:@([UIScreen mainScreen].bounds.size.width) forKeyPath:@"_recognizer._settings._edgeSettings._edgeRegionSize"];

效果如下
好吧 我不会传动态图
今天看了一下昨天遗留下的问题 就是在xib 继承基类是 导航条宽度比屏幕要宽,现在解决的

就是在创建 navgationBar 是frame的宽 不能写 self.view.bounds,size.width

应该写成
self.NavigationBar = [[UINavigationBar alloc]initWithFrame:CGRectMake(0,0,[UIScreen mainScreen].bounds.size.width,64)];

不上图了 哈哈哈哈

这些问题都解决了 还有一个就是 之所以自定义导航栏有一个原因是 不同的iOS系统版本 导航栏格式不同 自定义是为了统一格式. 很显然 我一上的自定义办法不太好 达不到这样的效果啊

========================

系统自带的向右滑动手势返回上一个界面,ios7--手势

 
当从控制器A push到控制器B,我们返回控制器A,除了使用按钮返回 
[self.navigationController pushViewController:Vc animated:YES];

还可以使用ios7出来的向右滑动,返回控制器A
文档中是这样定义的:
@property(nullable, nonatomic, weak) id<UINavigationControllerDelegate> delegate;

@property(nullable, nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED;

----------------------------------------------------------------------
我们在控制器B中的viewDidLoad中
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
self.navigationController.interactivePopGestureRecognizer.enabled = YES;      // 手势有效设置为YES  无效为NO
self.navigationController.interactivePopGestureRecognizer.delegate = self;    // 手势的代理设置为self
}

但是当回到控制器A中时,再想push到控制器B,就会出现卡屏,不会动的现象,因为rootView也会有向右滑动返回的问题
要解决这个问题,我们只需在控制器A的viewDidAppear中设置,interactivePopGestureRecognizer为NO:
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}

}

这样即可以保证再B中向右滑返回A动后再次pushB时不会卡在A界面。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios 全屏返回