问题铺垫
在iOS中UIResponder类是专门用来响应用户的操作处理各种事件的,包括触摸事件(Touch Events)、运动事件(Motion Events)、远程控制事件(Remote Control Events,如插入耳机调节音量触发的事件)。我们知道UIApplication、UIView、UIViewController这几个类是直接继承自UIResponder,UIWindow是直接继承自UIView的一个特殊的View,所以这些类都可以响应事件。当然我们自定义的继承自UIView的View以及自定义的继承自UIViewController的控制器都可以响应事件。iOS里面通常将这些能响应事件的对象称之为响应者。
UIResponder中定义了一系列对事件的处理方法,他们分别是:
–(void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event
–(void)touchesMoved:(NSSet )touches withEvent:(UIEvent )event
–(void)touchesEnded:(NSSet )touches withEvent:(UIEvent )event
–(void)touchesCancelled:(NSSet )touches withEvent:(UIEvent )event
从方法名字可以知道,他们分别对应了屏幕事件的开始、移动、结束和取消几个阶段,前三个阶段理解都没问题,最后一个取消事件的触发时机是在诸如突然来电话或是系统杀进程时调用。这些方法的第一个参数定义了UITouch对象的一个集合(NSSet),它的数量表示了这次事件是几个手指的操作,目前iOS设备支持的多点操作手指数最多是5。第二个参数是当前的UIEvent对象。
问题:放在scrollview上的view 都不能响应touch 事件
原因:(也是UIScrollerView 原理)
UIScrollView的工作原理,当手指touch的时候,UIScrollView会拦截Event,会等待一段时间,在这段时间内,如果没有手指 没有移动,当时间结束时,UIScrollView会发送tracking events到子视图上。在时间结束前,手指发生了移动,那么UIScrollView就会进行移动,从而取消发送tracking。一句话就是:touch 事件被UIScrollView拦截了,即UIScrollView成为touch 事件的响应者,进而事件不在向下传递。
解决方案
网上好多文章都建议给UIScrollView添加一个分类UIScrollView (UITouch)重写上述方法。这看上去是个不错的选择,如果你不使用手写键盘应该也没问题。使用了手写键盘就会闪退,控制台输出如下:
[UIKBBlurredKeyView candidateList]: unrecognized selector sent to instance
这是手势冲突所致的,可以添加一层判断,判断是否将消息传递给子视图。代码如下:
//父视图是否可以将消息传递给子视图,YES是将事件传递给子视图,则不滚动,NO是不传递则继续滚动
- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view
{
if ([view isKindOfClass:[UIScrollView class]])
{
return NO;
}
else
{
return YES;
}
}
另一个解决方法是:在UIScrollView上面加一个UIView,通过在view上面的手势来改变键盘。多了一层隔离,但业务逻辑简单。
UITapGestureRecognizer *tapGr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard:)];
tapGr.cancelsTouchesInView = NO;
[backView addGestureRecognizer:tapGr];