问题
最近有有朋友在Github上提出issue说 KSPhotoBrowser 在图片长度超出屏幕大小多时候没有办法通过拖拽返回。其实这个问题从我开始开发的时候就存在,感觉影响不是很大就一直拖着没解决。既然有人提出来了,那看来是必须得解决一下了。。。
第一张图就是正常的拖拽返回,第二张有问题。
解决
要解决这个问题我们不得不说下 KSPhotoBrowser 的布局结构,先来看一张图:
如上图所示,一个父 UIScrollView 里面添加了多个子 UIScrollView,每个子 UIScrollView 里包含一个 UIImageView 来显示图片,可以通过左右滑动父 UIScrollView 来查看不同的图片。单击、双击和拖动手势是添加在父 UIScrollView 的 SuperView 上,也就是 ViewController 的根 View 之上。
当图片的高宽比小于屏幕大高宽比的时候,拖动手势可以正常触发(如上图1所示)。但是当图片的高宽比大于屏幕的高宽比的时候(也就是图片比较长),拖动手势就无法正常触发(如上图2所示)。
经过查看 UIScrollView 的头文件发现 UIScrollView 内部也有一个拖动手势:
1
2
3
4
5
// 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 的头文件我们发现下面这个代理方法:
1
2
// 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 的拖动手势的代理方法,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 判断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;
}
运行一下代码发现问题完美解决: