iOS事件拦截及应用
1.概述
我们知道事件的分发是由Application到Window再到各级View的,所以显然最安全可靠的拦截地方是Application。这里拦截事件后如果不手动往下分发,则进入hit-test View过程的机会都没有。
UIApplication和UIWindow都有sendEvent:方法,用来分发Event。我们可以继承类,重新实现sendEvent:方法,这样就可以拦截下事件,完成一些特殊的处理,也可以利用runtime,将sendEvent:方法替换为我们自己的方法,添加一些我们自己的实现,然后再调用原来的消息流程。
2.拦截方式
1)利用runtime
#import "EventHookObject.h" #import <objc/objc.h> #import <objc/runtime.h> @implementation EventHookObject + (void)initialize { Method sendEvent = class_getInstanceMethod([UIApplication class], @selector(sendEvent:)); Method mySendEvent = class_getInstanceMethod([self class], @selector(mySendEvent:)); IMP sendEventIMP = method_getImplementation(sendEvent); class_addMethod([UIApplication class], @selector(sendEventOri:), sendEventIMP, method_getTypeEncoding(sendEvent)); IMP mySendEventIMP = method_getImplementation(mySendEvent); class_replaceMethod([UIApplication class], @selector(sendEvent:), mySendEventIMP, method_getTypeEncoding(sendEvent)); } /* * 截获到UIApplication的sendEvent * 我们可以先处理完以后,再继续调用正常处理流程 */ - (void)mySendEvent:(UIEvent*)event { //这里可以添加一些你想要的处理 [self performSelector:@selector(sendEventOri:) withObject:event]; }
然后就是在函数- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions内调用即可
2)继承UIApplication类,实现自己的sendEvent:
应用场景如下,比如应用要求在非某个特定view的区域触摸时进行一项处理。
我们当然可以在其余每一个view里面增加代码进行判断,不过这样比较累,容易漏掉一些地方,比较简单的解决方案就是在继承UIApplication类,实现自己的sendEvent:,在这个方法里面初步过滤一下事件,是触摸事件就发送Notification,而特定的view会注册这个Notification,收到后判断一下是否触摸到了自己之外的区域。
#import "EventApplication.h" NSString *const NotificationScreenTouch = @"NotificationScreenTouch"; @implementation EventApplication - (void)sendEvent:(UIEvent *)event { if (event.type == UIEventTypeTouches) { if ([[event.allTouches anyObject] phase] == UITouchPhaseBegan) { [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:NotificationScreenTouch object:nil userInfo:[NSDictionary dictionaryWithObject:event forKey:@"value"]]]; } } [super sendEvent:event]; } @end
在main.m文件中替换掉UIApplication的调用
#import <UIKit/UIKit.h> #import "AppDelegate.h" #import "EventApplication.h" int main(int argc, char *argv[]) { @autoreleasepool { //return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); return UIApplicationMain(argc, argv, NSStringFromClass([EventApplication class]), NSStringFromClass([AppDelegate class])); } }
这样我们就实现了事件的预处理,抓住了源头,想做啥都可以。