参考:
情景:
如果你有一个全屏、透明的UIView盖在目前界面的上面,这个uivew不做显示用,只是收集用户在屏幕上的move 时的点。但要允许用户在点击和长按时,全屏view背后的view做出反应,这时全屏的view相当于不存在。
解决方案:
方案一
设置该全屏、透明view的 userInteractionEnabled 为NO,截获UIApplication的SendEvent ,在其中记录move时的所有点。这时,用户所有的touch事件都按原来执行,这个全屏的view真像不存在一样。
利用objective-c runtime相关代码核心实现如下:
//Swap the implementations of our interceptor and the original sendEvent:
Method oldMethod = class_getInstanceMethod(self, @selector(sendEvent:));
Method newMethod = class_getInstanceMethod(self, @selector(interceptSendEvent:));
method_exchangeImplementations(oldMethod, newMethod);
简单的说就是创建一个UIApplication的类别,然后通过method_exchangeImplementations 替换sendEvent,让系统调用sendEvent时首先调用我们自己定义的方法,然后在其中记录move的point点,最后调用系统原来的sendEvent方法即可。
方案二
设置该全屏、透明view的 userInteractionEnabled 为YES,重写父类的四个事件处理方法
- (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;
- (void)forwardTouchBegan:(id)obj
{
NSArray* array = (NSArray*)obj;
NSSet* touches = [array objectAtIndex:0];
UIEvent* event = [array objectAtIndex:1];
[[self nextResponder] touchesBegan:touches withEvent:event];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
touchMove_ = NO;
[self performSelector:@selector(forwardTouchBegan:) withObject:[NSArray arrayWithObjects:touches,event,nil] afterDelay:0.5];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
touchMove_ = YES;
[[self class] cancelPreviousPerformRequestsWithTarget:self];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!touchMove_)
{
[[self nextResponder] touchesEnded:touches withEvent:event];
}
}
这里主要应用了这两个方法,在begin时延时调用forwardTouchBegan ,在move时取消 调用。这里用延时方法有效地解决了 无法判断用户touch begin时接下去是否会move的问题。 参考【1】中列出了苹果文档中利用此方法来解决 单击还是双击的问题,只不过这时在touch end启动延时调用performSelector ,在touch begin时根据tapcount判断是否取消调用。
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
不过方案二 跟 背景view与全屏viewview层次相关,如果两则不同的window 则 通过[self nextResponder] 背景view可能还是接收不到事件。