[iOS] 解决MMDrawController与scrollView手势共存问题
2017-11-14 21:48
218 查看
前言
在写代码过程中,遇到一个问题,使用抽屉控制器实现抽屉效果时,如果主控制器含有 scrollView,那么 scrollView 的滑动会与 MMDrawController 的滑动产生冲突,从而无法实现抽屉效果,经过尝试找到两种解决办法,写了一个简单 Demo。这里就简单的介绍一下 MMDrawController 的使用及如何解决 MMDrawController 与 scrollView 手势共存问题。
MMDrawController的简单介绍
MMDrawController 是一个非常实用的抽屉控制器第三方库,详情使用请参考 GitHub-MMDrawController 。Demo 中减掉一些不常用的功能,实现抽屉效果使用MMDrawController分为两步:
1.设置抽屉控制器。
2.展示抽屉控制器。
第一步设置抽屉控制器
在 AppDelegate 的 didFinishLaunchingWithOptions 方法中,设置显示抽屉控制器。 1.初始化控制器(左抽屉控制器、右抽屉控制器、中间导航主控制器)。
2.使用 MMDrawController 实现左右抽屉效果。
3.设置打开/关闭抽屉的手势。
4.设置左右抽屉控制器的显示宽度。
5.显示抽屉控制器。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //1.初始化控制器 UIViewController *centerVC = [[drawerDemoCenterViewController alloc]init]; UIViewController *leftVC = [[drawerDemoLeftViewController alloc]init]; UIViewController *rightVC = [[drawerDemoLRightViewController alloc]init]; UINavigationController *centerNavVC = [[UINavigationController alloc]initWithRootViewController:centerVC]; //2.使用MMDrawerController self.drawerController = [[MMDrawerController alloc]initWithCenterViewController:centerNavVC leftDrawerViewController:leftVC rightDrawerViewController:rightVC]; //3.设置打开/关闭抽屉的手势 self.drawerController.openDrawerGestureModeMask = MMOpenDrawerGestureModeAll; self.drawerController.closeDrawerGestureModeMask =MMCloseDrawerGestureModeAll; //4.设置左右两边抽屉显示的宽度 self.drawerController.maximumLeftDrawerWidth = 200.0; self.drawerController.maximumRightDrawerWidth = 300.0; //5、初始化窗口、设置根控制器、显示窗口 self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds]; [self.window setRootViewController:self.drawerController]; [self.window makeKeyAndVisible]; return YES;
第二步展示抽屉控制器
在主控制器(centerNavVC.m)中,按钮点击展示抽屉控制器。设置左右导航按钮。
设置点击事件:展示抽屉效果。
///设置左右导航按钮 - (void)setupNavgation { self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"左抽屉" style:UIBarButtonItemStylePlain target:self action:"didTapLeftMenuButton"]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"右抽屉" style:UIBarButtonItemStylePlain target:self action:"didTapLeftMenuButton"]; } ///设置点击事件:展示抽屉效果 - (void)didTapLeftBarButton { //展示左抽屉控制器 [self.mm_drawerController toggleDrawerSide:MMDrawerSideLeft animated:YES completion:nil]; } - (void)didTapRightBarButton { //展示右抽屉控制器 [self.mm_drawerController toggleDrawerSide:MMDrawerSideRight animated:YES completion:nil]; }
Gif示例:
一个简单的抽屉控制器就完成了,如图所示:1.可以通过左右滑动,在主控制器左右展示抽屉控制器。
2.也可以通过点击左右导航栏中按钮对抽屉控制器进行展示。
MMDrawControllerscrollView 手势共存问题
问题描述
实现抽屉效果时,如果主控制器含有 scrollView,那么 scrollView 的滑动会与 MMDrawController 的滑动产生冲突,,从而无法实现抽屉效果。设置 ScrollView
在主控制器中添加 scrollView 的步骤大概为以下三步:1.创建 ScrollView 及三个子 View:设置尺寸、颜色:
UIView *view1 = [[UIView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH,SCREEN_HEIGHT)]; UIView *view2 = [[UIView alloc]initWithFrame:CGRectMake(1 * SCREEN_WIDTH, 0, SCREEN_WIDTH,SCREEN_HEIGHT)]; UIView *view3 = [[UIView alloc]initWithFrame:CGRectMake(2 * SCREEN_WIDTH, 0, SCREEN_WIDTH,SCREEN_HEIGHT)]; self.contentScrollView.frame = self.view.frame; view1.backgroundColor = [UIColor redColor]; view2.backgroundColor = [UIColor yellowColor]; view3.backgroundColor = [UIColor blueColor];
2.添加 ScrollView 及三个子 View:
[self.view addSubview:self.contentScrollView]; [self.contentScrollView addSubview:view1]; [self.contentScrollView addSubview:view2]; [self.contentScrollView addSubview:view3];
3.设置 scrollView 属性,关闭弹簧效果 、 启动分页效果 、 滚动范围为3倍屏幕宽度:
self.contentScrollView.bounces = NO; self.contentScrollView.pagingEnabled = YES; self.contentScrollView.contentSize = CGSizeMake(self.view.frame.size.width * 3,0); }
Gif示例:
启动模拟器后,显示如图所示:1.scrollView 可以正常滑动。
2.点击抽屉按钮,抽屉效果显示正常。
3.左右滑动的时候抽屉效果失效。
分析原因:
scrollView 中有拖拽手势,抽屉控制器也有拖拽手势两个手势冲突,导致抽屉效果失效。解决办法1:
方法1:使用 scrollView 代理方法,此方法中有一个参数: targetContentOffset ,可以通过 targetContentOffset 变量来获取滑动偏移量,若偏移量X方向- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { }
该方法使用条件是:
- 控制器成为 ScrollView 的代理,准守代理协议
该方法使用场景:
- 滑动 scrollView,并且手指离开时执行。一次有效滑动,只执行一次。
该方法功能介绍:
- 该方法中有一个非常重要的参数 targetContentOffset 滑动位移指针。
通过该指针 targetContentOffset->x 获取,手指离开时 scrollView 在 X 方向滑动偏移量: tagetX
使用 scrollView 在 X 方向滑动偏移量 tagetX,进行判断:
1.1 若:滑动后tagetX为最小(最小的 X 值:0) 并且 显示在 ScrollView 最左侧(contentOffsetX 为最小值:0)
展开左抽屉效果。
1.2 若:滑动后 tagetX 为最大(最大的 X 值:2倍屏幕宽度) 并且 显示在 ScrollView 最左侧(contentOffsetX 为最小值:0)
展开右抽屉效果。
1.3 若:不满足以上两项,则不进行抽屉效果展示。
代码示例
//成为代理,准守协议,执行方法 self.contentScrollView.delegate = self; - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { //获取滑动偏移量 float tagetX = targetContentOffset->x; //向左滑动时: 如果滑动后的X为最小(最小的X值),并且 为第一个控制器(contentOffsetX 为最小0) //向右滑动时: 如果滑动后的X为最大(最大的X值),并且为最后一个控制器(contentOffsetX 为最大) if (tagetX == 0 && self.contentScrollView.contentOffset.x == 0 * SCREEN_WIDTH) { [self.mm_drawerController toggleDrawerSide:MMDrawerSideLeft animated:YES completion:nil]; } else if (tagetX == 2 * SCREEN_WIDTH && self.contentScrollView.contentOffset.x == 2 * SCREEN_WIDTH) { [self.mm_drawerController toggleDrawerSide:MMDrawerSideRight animated:YES completion:nil]; } }
解决办法2:
方法2:给 scrollView 添加一个空手势,使用手势的代理方法。- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {}
该方法使用条件是:
- 控制器成为手势的代理,准守代理协议。
该方法使用场景:
- 当执行手势时.会调用该方法。
该方法功能介绍:
当执行手势时,询问该手势接收者是否应该开始执行执行手势方法:
若:return YES ,该手势会被拦截(不会传递给 ScrollView 进行滚动)。
若:return NO, 该手势会传递下去(传递给 ScrollView 进行滚动)。
在该方法中进行判断:
1 若:手势不为滑动手势,直接 return NO,该手势会传递下去(传递给 ScrollView 进行滚动)。
2 若:手势为滑动手势,在进行判断:
2.1 若:该滑动手势滑动距离(滑动方向向右并且此时 scrollView 的 X 在最左边),展开左抽屉效果;
展开左抽屉效果。
return YES,该手势会被拦截(不会传递给ScrollView进行滚动)。
2.2 若:该滑动手势滑动距离(滑动方向向做并且此时 scrollView 的 X 在最右边),展开右抽屉效果;
展开右抽屉效果。
return YES,该手势会被拦截(不会传递给ScrollView进行滚动)。
2.3 若:不满足以上两点,则return NO,该手势会传递下去(传递给ScrollView进行滚动)。
代码示例
//创建一个空手势 UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:nil]; //添加空手势 [self.contentScrollView addGestureRecognizer:pan]; //成为代理,准守协议,执行代理方法 pan.delegate = self; - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { // 判断手势是否为 滑动手势, if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) { UIPanGestureRecognizer *pan = (UIPanGestureRecognizer *)gestureRecognizer; CGPoint pos = [pan velocityInView:pan.view]; //判断是否展开左侧或右侧抽屉控制器 if (pos.x > SCREEN_WIDTH /3 && self.contentScrollView.contentOffset.x == 0 * SCREEN_WIDTH) { [self.mm_drawerController toggleDrawerSide:MMDrawerSideLeft animated:YES completion:nil]; return YES; } else if (pos.x < -SCREEN_WIDTH /3 && self.contentScrollView.contentOffset.x == 2 * SCREEN_WIDTH) { [self.mm_drawerController toggleDrawerSide:MMDrawerSideRight animated:YES completion:nil]; return YES; } } return NO; }
Gif示例:
启动模拟器后,显示如图所示:1.scrollView 可以正常滑动。
2.点击抽屉按钮,抽屉效果显示正常。
3.左右滑动 scrollView 抽屉控制器与scrollView 不冲突。
![](http://blog.qiji.tech/wp-content/uploads/2017/10/Gif.ScrollViewDemo.gif)
小结
解决该问题的核心为是在ScrollView左右边缘进行滑动时,判断是否应该执行抽屉效果。这里把两个方法的思路梳理了一下。流程图如下:
![](http://blog.qiji.tech/wp-content/uploads/2017/10/111-4.png)
相关文章推荐
- iOS 当使用FD_FullscreenPopViewController的时候遇到scrollView右滑手势无法使用的解决
- iOS MMDrawerController侧滑菜单与中心视图手势冲突问题的解决方案(续--点击可收起抽屉)
- Android 解决ListView 和 ScrollView 共存冲突的问题
- 解决Android ListView 和 ScrollView 共存时冲突 问题 方法其一
- Android 解决ListView 和 ScrollView 共存冲突的问题
- iOS - UINavigationController下的ScrollView相关问题
- IOS UISearchDisplayController 点击搜索出现黑条问题解决方案
- ios 处理侧滑和图片浏览器scrollView的手势冲突问题
- 猫猫学iOS 之控制器view显示中view的父子关系及controller的父子关系_解决屏幕旋转不能传递事件问题
- ios 开发日记17-单击手势和双击手势共存问题
- IOS UISearchDisplayController 点击搜索出现黑条问题解决方案
- iOS开发-BUG:解决在使用MPMoviePlayerController播放视频后导航栏上移的问题
- Android 解决ListView 和 ScrollView 共存冲突的问题
- 关于iOS页面中scrollview中嵌入百度地图滑动冲突问题解决方法
- 解决listView和scrollView的共存问题
- Android 解决ListView 和 ScrollView 共存冲突的问题
- iOS 之控制器view显示中view的父子关系及controller的父子关系_解决屏幕旋转不能传递事件问题
- iOS横向滚动的scrollView和系统pop手势返回冲突的解决办法
- 解决scrollview与listview共存问题 计算listview的宽高(item中有imageview)
- AJ学IOS 之控制器view显示中view的父子关系及controller的父子关系_解决屏幕旋转不能传递事件问题