CODE & ZEN

凯旋的博客

我是孙凯旋 @skx926
一名来自中国的 iOS 开发者
写过 Android
也会点 Web
深知付出更多才能收获更多
唯有在技术的道路上勤勤恳恳
方得片刻安心


嵌套UIScrollView滑动手势冲突的解决

问题

最近有有朋友在Github上提出issue说 KSPhotoBrowser 在图片长度超出屏幕大小多时候没有办法通过拖拽返回。其实这个问题从我开始开发的时候就存在,感觉影响不是很大就一直拖着没解决。既然有人提出来了,那看来是必须得解决一下了。。。

第一张图就是正常的拖拽返回,第二张有问题。

解决

要解决这个问题我们不得不说下 KSPhotoBrowser 的布局结构,先来看一张图:

如上图所示,一个父 UIScrollView 里面添加了多个子 UIScrollView,每个子 UIScrollView 里包含一个 UIImageView 来显示图片,可以通过左右滑动父 UIScrollView 来查看不同的图片。单击、双击和拖动手势是添加在父 UIScrollView 的 SuperView 上,也就是 ViewController 的根 View 之上。

当图片的高宽比小于屏幕大高宽比的时候,拖动手势可以正常触发(如上图1所示)。但是当图片的高宽比大于屏幕的高宽比的时候(也就是图片比较长),拖动手势就无法正常触发(如上图2所示)。

经过查看 UIScrollView 的头文件发现 UIScrollView 内部也有一个拖动手势:

// Use these accessors to configure the scroll view's built-in gesture recognizers.
// Do not change the gestures' delegates or override the getters for these properties.

// Change `panGestureRecognizer.allowedTouchTypes` to limit scrolling to a particular set of touch types.
@property(nonatomic, readonly) UIPanGestureRecognizer *panGestureRecognizer NS_AVAILABLE_IOS(5_0);

所以当我们往下拖动的时候 UIScrollView 的拖动手势拦截了事件,使其无法传递给下方的 View 上的手势。那我们能不能根据滑动时 UIScrollView 的 contentOffset 和滑动的方向来决定触发哪一个手势呢?答案是肯定的,通过查看 UIGestureRecognizer 的头文件我们发现下面这个代理方法:

// called when a gesture recognizer attempts to transition out of UIGestureRecognizerStatePossible. returning NO causes it to transition to UIGestureRecognizerStateFailed
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;

利用这个方法我们就可以在手势识别阶段 UIGestureRecognizerStatePossible 根据 UIScrollView 的 contentOffset 和滑动的方向来决定要不要触发 UIScrollView 的手势,如果我们 return NO,事件就会正常传递给下面的 View 上的手势,从而解决问题。

所以现在我们使持有 UIImageView 的 KSPhotoView 继承自 UIScrollView 并重写 UIScrollView 的拖动手势的代理方法,代码如下:

// 判断scrollView是不是在最顶部往下滑或者在最底部往上滑,如果是这两种情况才需要把事件往下传递
- (BOOL)isScrollViewOnTopOrBottom {
CGPoint translation = [self.panGestureRecognizer translationInView:self];
if (translation.y > 0 && self.contentOffset.y <= 0) {
return YES;
}
CGFloat maxOffsetY = floor(self.contentSize.height - self.bounds.size.height);
if (translation.y < 0 && self.contentOffset.y >= maxOffsetY) {
return YES;
}
return NO;
}

#pragma mark - GestureRecognizerDelegate

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer == self.panGestureRecognizer) {
if (gestureRecognizer.state == UIGestureRecognizerStatePossible) {
if ([self isScrollViewOnTopOrBottom]) {
return NO;
}
}
}
return YES;
}

运行一下代码发现问题完美解决:

最近的文章

制作服务端推送时使用的.pem证书

服务端要给移动端发送推送消息就需要用到推送证书,但是服务端通常需要的证书 .pem 格式的,并不是我们从Apple的开发者中心下载到的 .cer 格式的文件,所以我们需要对其做一个转换。 申请推送证书的过程我就不赘述了,这里假设你已经申请好了开发环境和生产环境的推送证书并下载导入到 Mac 钥匙串当 …

于  iOS开发 继续阅读
更早的文章

Xcode多Target下本地化App名称

Target 可能很多人都会有开发多个相似的App的需求,这些相似的App可能也就是名称、BundleID、证书配置不同,其他的功能都基本一样。对于这种情况的处理,一种比较笨的办法就是手动修改 Info.plist 文件中相应的内容,打包完了之后再次修改再打包。这种方法如果只是搞一次也不会麻烦很多, …

于  iOS开发 继续阅读
comments powered by Disqus