Skip to content

Commit

Permalink
Fixed Cmd-R shortcut on iOS 9
Browse files Browse the repository at this point in the history
  • Loading branch information
nicklockwood committed Jun 22, 2015
1 parent 457fca4 commit eda44ed
Showing 1 changed file with 92 additions and 25 deletions.
117 changes: 92 additions & 25 deletions React/Base/RCTKeyCommands.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,16 @@

#import <UIKit/UIKit.h>

#import "RCTDefines.h"
#import "RCTUtils.h"

#if RCT_DEV

static BOOL RCTIsIOS8OrEarlier()
{
return [UIDevice currentDevice].systemVersion.floatValue < 9;
}

@interface RCTKeyCommand : NSObject <NSCopying>

@property (nonatomic, strong) UIKeyCommand *keyCommand;
Expand All @@ -27,7 +35,7 @@ - (instancetype)initWithKeyCommand:(UIKeyCommand *)keyCommand
{
if ((self = [super init])) {
_keyCommand = keyCommand;
_block = block ?: ^(__unused UIKeyCommand *cmd) {};
_block = block;
}
return self;
}
Expand Down Expand Up @@ -58,29 +66,58 @@ - (BOOL)matchesInput:(NSString *)input flags:(UIKeyModifierFlags)flags
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 UIApplication (RCTKeyCommands)
@implementation UIResponder (RCTKeyCommands)

- (NSArray *)RCT_keyCommands
{
NSSet *commands = [RCTKeyCommands sharedInstance].commands;
return [[self RCT_keyCommands] arrayByAddingObjectsFromArray:
[[commands valueForKeyPath:@"keyCommand"] allObjects]];
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)

// 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];
}
Expand All @@ -91,9 +128,23 @@ @implementation RCTKeyCommands

+ (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
Expand Down Expand Up @@ -121,11 +172,11 @@ - (void)registerKeyCommandWithInput:(NSString *)input
{
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
Expand All @@ -136,19 +187,9 @@ - (void)registerKeyCommandWithInput:(NSString *)input
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
Expand Down Expand Up @@ -178,3 +219,29 @@ - (BOOL)isKeyCommandRegisteredForInput:(NSString *)input
}

@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

0 comments on commit eda44ed

Please sign in to comment.