參考:
[1]http://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/MultitouchEvents/MultitouchEvents.html#//apple_ref/doc/uid/TP40009541-CH3-SW20
情景:
如果你有一個全屏、透明的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可能還是接收不到事件。