diff --git a/React/Base/RCTKeyCommands.m b/React/Base/RCTKeyCommands.m index 387bfc1a9..a9d358482 100644 --- a/React/Base/RCTKeyCommands.m +++ b/React/Base/RCTKeyCommands.m @@ -11,8 +11,16 @@ #import +#import "RCTDefines.h" #import "RCTUtils.h" +#if RCT_DEV + +static BOOL RCTIsIOS8OrEarlier() +{ + return [UIDevice currentDevice].systemVersion.floatValue < 9; +} + @interface RCTKeyCommand : NSObject @property (nonatomic, strong) UIKeyCommand *keyCommand; @@ -27,7 +35,7 @@ { if ((self = [super init])) { _keyCommand = keyCommand; - _block = block ?: ^(__unused UIKeyCommand *cmd) {}; + _block = block; } return self; } @@ -58,29 +66,58 @@ RCT_NOT_IMPLEMENTED(-init) return [_keyCommand.input isEqual:input] && _keyCommand.modifierFlags == flags; } +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@:%p input=\"%@\" flags=%zd hasBlock=%@>", + [self class], self, _keyCommand.input, _keyCommand.modifierFlags, + _block ? @"YES" : @"NO"]; +} + @end @interface RCTKeyCommands () @property (nonatomic, strong) NSMutableSet *commands; -- (BOOL)RCT_handleKeyCommand:(UIKeyCommand *)key; +@end + +@implementation UIResponder (RCTKeyCommands) + +- (NSArray *)RCT_keyCommands +{ + NSSet *commands = [RCTKeyCommands sharedInstance].commands; + return [[commands valueForKeyPath:@"keyCommand"] allObjects]; +} + +- (void)RCT_handleKeyCommand:(UIKeyCommand *)key +{ + // NOTE: throttle the key handler because on iOS 9 the handleKeyCommand: + // method gets called repeatedly if the command key is held down. + + static NSTimeInterval lastCommand = 0; + if (RCTIsIOS8OrEarlier() || CACurrentMediaTime() - lastCommand > 0.5) { + for (RCTKeyCommand *command in [RCTKeyCommands sharedInstance].commands) { + if ([command.keyCommand.input isEqualToString:key.input] && + command.keyCommand.modifierFlags == key.modifierFlags) { + if (command.block) { + command.block(key); + lastCommand = CACurrentMediaTime(); + } + } + } + } +} @end @implementation UIApplication (RCTKeyCommands) -- (NSArray *)RCT_keyCommands -{ - NSSet *commands = [RCTKeyCommands sharedInstance].commands; - return [[self RCT_keyCommands] arrayByAddingObjectsFromArray: - [[commands valueForKeyPath:@"keyCommand"] allObjects]]; -} - +// Required for iOS 8.x - (BOOL)RCT_sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event { if (action == @selector(RCT_handleKeyCommand:)) { - return [[RCTKeyCommands sharedInstance] RCT_handleKeyCommand:sender]; + [self RCT_handleKeyCommand:sender]; + return YES; } return [self RCT_sendAction:action to:target from:sender forEvent:event]; } @@ -91,9 +128,23 @@ RCT_NOT_IMPLEMENTED(-init) + (void)initialize { - //swizzle UIApplication - RCTSwapInstanceMethods([UIApplication class], @selector(keyCommands), @selector(RCT_keyCommands)); - RCTSwapInstanceMethods([UIApplication class], @selector(sendAction:to:from:forEvent:), @selector(RCT_sendAction:to:from:forEvent:)); + if (RCTIsIOS8OrEarlier()) { + + //swizzle UIApplication + RCTSwapInstanceMethods([UIApplication class], + @selector(keyCommands), + @selector(RCT_keyCommands)); + + RCTSwapInstanceMethods([UIApplication class], + @selector(sendAction:to:from:forEvent:), + @selector(RCT_sendAction:to:from:forEvent:)); + } else { + + //swizzle UIResponder + RCTSwapInstanceMethods([UIResponder class], + @selector(keyCommands), + @selector(RCT_keyCommands)); + } } + (instancetype)sharedInstance @@ -121,11 +172,11 @@ RCT_NOT_IMPLEMENTED(-init) { RCTAssertMainThread(); - if (input.length && flags) { + if (input.length && flags && RCTIsIOS8OrEarlier()) { // Workaround around the first cmd not working: http://openradar.appspot.com/19613391 // You can register just the cmd key and do nothing. This ensures that - // command-key modified commands will work first time. + // command-key modified commands will work first time. Fixed in iOS 9. [self registerKeyCommandWithInput:@"" modifierFlags:flags @@ -136,19 +187,9 @@ RCT_NOT_IMPLEMENTED(-init) modifierFlags:flags action:@selector(RCT_handleKeyCommand:)]; - [_commands addObject:[[RCTKeyCommand alloc] initWithKeyCommand:command block:block]]; -} - -- (BOOL)RCT_handleKeyCommand:(UIKeyCommand *)key -{ - for (RCTKeyCommand *command in [RCTKeyCommands sharedInstance].commands) { - if ([command.keyCommand.input isEqualToString:key.input] && - command.keyCommand.modifierFlags == key.modifierFlags) { - command.block(key); - return YES; - } - } - return NO; + RCTKeyCommand *keyCommand = [[RCTKeyCommand alloc] initWithKeyCommand:command block:block]; + [_commands removeObject:keyCommand]; + [_commands addObject:keyCommand]; } - (void)unregisterKeyCommandWithInput:(NSString *)input @@ -178,3 +219,29 @@ RCT_NOT_IMPLEMENTED(-init) } @end + +#else + +@implementation RCTKeyCommands + ++ (instancetype)sharedInstance +{ + return nil; +} + +- (void)registerKeyCommandWithInput:(NSString *)input + modifierFlags:(UIKeyModifierFlags)flags + action:(void (^)(UIKeyCommand *))block {} + +- (void)unregisterKeyCommandWithInput:(NSString *)input + modifierFlags:(UIKeyModifierFlags)flags {} + +- (BOOL)isKeyCommandRegisteredForInput:(NSString *)input + modifierFlags:(UIKeyModifierFlags)flags +{ + return NO; +} + +@end + +#endif