I’m not even home from the more-than-excellent NSConference in Leicester, but had to hack on something super awesome that Evan Doll of Flipboard presented earlier today. They added keyboard support to UIAlertView and UIActionSheet (amongst other things) – simply to make debugging in the Simulator faster by accepting Esc and Enter keys – something that Apple should have done anyway. There’s not much value in shipping this in release builds, except better support for bluetooth keyboards. And since this hack uses private API AND accesses a struct with a memory layout that could change, I don’t recommend shipping it. If you do, make sure that you whitelist iOS versions and block this method by default on unknown future versions of iOS. I’m using it in PSPDFKit only when compiled in DEBUG mode for the Simulator.
#define GSEVENT_TYPE 2#define GSEVENT_FLAGS 12#define GSEVENTKEY_KEYCODE 15#define GSEVENT_TYPE_KEYUP 10// http://nacho4d-nacho4d.blogspot.co.uk/2012/01/catching-keyboard-events-in-ios.html__attribute__((constructor))staticvoidPSPDFKitAddKeyboardSupportForUIAlertView(void){@autoreleasepool{// Hook into sendEvent: to get keyboard events.SELsendEventSEL=NSSelectorFromString(@"pspdf_sendEvent:");IMPsendEventIMP=imp_implementationWithBlock(^(id_self,UIEvent*event){objc_msgSend(_self,sendEventSEL,event);// call original implementation.SELgsEventSEL=NSSelectorFromString([NSStringstringWithFormat:@"%@%@Event",@"_",@"gs"]);if([eventrespondsToSelector:gsEventSEL]){// Key events come in form of UIInternalEvents.// They contain a GSEvent object which contains a GSEventRecord among other things.int*eventMem=(int*)[eventperformSelector:gsEventSEL];if(eventMem){if(eventMem[GSEVENT_TYPE]==GSEVENT_TYPE_KEYUP){UniChar*keycode=(UniChar*)&(eventMem[GSEVENTKEY_KEYCODE]);inteventFlags=eventMem[GSEVENT_FLAGS];//NSLog(@"Pressed %d", *keycode);[[NSNotificationCenterdefaultCenter]postNotificationName:@"PSPDFKeyboardEventNotification"object:niluserInfo:@{@"keycode":@(*keycode),@"eventFlags":@(eventFlags)}];}}}});PSPDFReplaceMethod(UIApplication.class,@selector(sendEvent:),sendEventSEL,sendEventIMP);// Add keyboard handler for UIAlertView.SELdidMoveToWindowSEL=NSSelectorFromString(@"pspdf_didMoveToWindow");IMPdidMoveToWindowIMP=imp_implementationWithBlock(^(UIAlertView*_self,UIEvent*event){objc_msgSend(_self,didMoveToWindowSEL,event);// call original implementation.staticcharkPSPDFKeyboardEventToken;if(_self.window){idobserverToken=[[NSNotificationCenterdefaultCenter]addObserverForName:@"PSPDFKeyboardEventNotification"object:nilqueue:nilusingBlock:^(NSNotification*notification){NSUIntegerkeyCode=[notification.userInfo[@"keycode"]integerValue];if(keyCode==41)/* ESC */{[_selfdismissWithClickedButtonIndex:_self.cancelButtonIndexanimated:YES];}elseif(keyCode==40)/* ENTER */{[_selfdismissWithClickedButtonIndex:_self.numberOfButtons-1animated:YES];}}];objc_setAssociatedObject(_self,&kPSPDFKeyboardEventToken,observerToken,OBJC_ASSOCIATION_RETAIN_NONATOMIC);}else{idobserverToken=objc_getAssociatedObject(_self,&kPSPDFKeyboardEventToken);if(observerToken)[[NSNotificationCenterdefaultCenter]removeObserver:observerToken];}});PSPDFReplaceMethod(UIAlertView.class,@selector(didMoveToWindow),didMoveToWindowSEL,didMoveToWindowIMP);}}
You will also need some swizzling helpers. Here’s what I use:
1234567891011121314151617
// http://www.mikeash.com/pyblog/friday-qa-2010-01-29-method-replacement-for-fun-and-profit.htmlstaticvoidPSPDFSwizzleMethod(Classc,SELorig,SELnew){MethodorigMethod=class_getInstanceMethod(c,orig);MethodnewMethod=class_getInstanceMethod(c,new);if(class_addMethod(c,orig,method_getImplementation(newMethod),method_getTypeEncoding(newMethod))){class_replaceMethod(c,new,method_getImplementation(origMethod),method_getTypeEncoding(origMethod));}else{method_exchangeImplementations(origMethod,newMethod);}}voidPSPDFReplaceMethod(Classc,SELorig,SELnewSel,IMPimpl){Methodmethod=class_getInstanceMethod(c,orig);if(!class_addMethod(c,newSel,impl,method_getTypeEncoding(method))){PSPDFLogError(@"Failed to add method: %@ on %@",NSStringFromSelector(newSel),c);}elsePSPDFSwizzleMethod(c,orig,newSel);}