iOS关于右滑手势返回上一级

首先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

@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

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

推荐阅读更多精彩内容