From d2ff48e2d82f6d373a47f63663903dd36a5a7a4a Mon Sep 17 00:00:00 2001 From: Tony Homer Date: Tue, 10 May 2016 13:01:23 -0400 Subject: [PATCH] Swizzle it just a little bit (#197) * initial implementation with swizzle tracking * remove swizzle tracking, add class prefix checking * reduce unnecessary string allocation in unswizzle * revert README.md to reenable cordova.plugins.Keyboard.hideKeyboardAccessoryBar * added attributions for swizzling approach * fixed indenting (tabs->spaces) * fixed indenting (tabs->spaces) * change approach from class swizzling to method swizzling * replaced c function to IMP cast with block-based imp * removed cruft --- README.md | 6 +- plugin.xml | 5 -- src/ios/IonicKeyboard.h | 5 +- src/ios/IonicKeyboard.m | 78 +++++++++++++++---------- src/ios/UIWebViewExtension.h | 4 -- src/ios/UIWebViewExtension.m | 109 ----------------------------------- www/ios/keyboard.js | 3 +- 7 files changed, 53 insertions(+), 157 deletions(-) delete mode 100644 src/ios/UIWebViewExtension.h delete mode 100644 src/ios/UIWebViewExtension.m diff --git a/README.md b/README.md index 6a4a654..61441ed 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ The `cordova.plugins.Keyboard` object provides functions to make interacting wit Methods ------- -- ~~cordova.plugins.Keyboard.hideKeyboardAccessoryBar~~ (**removed in 2.0, see below**) +- cordova.plugins.Keyboard.hideKeyboardAccessoryBar - cordova.plugins.Keyboard.close - cordova.plugins.Keyboard.disableScroll - cordova.plugins.Keyboard.show @@ -30,11 +30,9 @@ These events are fired on the window. # API reference -~~Keyboard.hideKeyboardAccessoryBar~~ +Keyboard.hideKeyboardAccessoryBar ================= -**NOTE: This method started causing apps to be rejected from the App Store, so has been removed until a workaround is found.** - Hide the keyboard accessory bar with the next, previous and done buttons. cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true); diff --git a/plugin.xml b/plugin.xml index 57a1d4f..372d790 100644 --- a/plugin.xml +++ b/plugin.xml @@ -38,13 +38,8 @@ - - diff --git a/src/ios/IonicKeyboard.h b/src/ios/IonicKeyboard.h index 773a46b..63935dc 100644 --- a/src/ios/IonicKeyboard.h +++ b/src/ios/IonicKeyboard.h @@ -1,11 +1,14 @@ #import +#import @interface IonicKeyboard : CDVPlugin { @protected id _keyboardShowObserver, _keyboardHideObserver; + IMP wkOriginalImp, uiOriginalImp, nilImp; + Method wkMethod, uiMethod; } -// @property (readwrite, assign) BOOL hideKeyboardAccessoryBar; +@property (readwrite, assign) BOOL hideKeyboardAccessoryBar; @property (readwrite, assign) BOOL disableScroll; //@property (readwrite, assign) BOOL styleDark; diff --git a/src/ios/IonicKeyboard.m b/src/ios/IonicKeyboard.m index cd3048d..d072ca7 100644 --- a/src/ios/IonicKeyboard.m +++ b/src/ios/IonicKeyboard.m @@ -4,20 +4,29 @@ @implementation IonicKeyboard -// @synthesize hideKeyboardAccessoryBar = _hideKeyboardAccessoryBar; +@synthesize hideKeyboardAccessoryBar = _hideKeyboardAccessoryBar; @synthesize disableScroll = _disableScroll; //@synthesize styleDark = _styleDark; - (void)pluginInitialize { - NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; - __weak IonicKeyboard* weakSelf = self; - + Class wkClass = NSClassFromString([@[@"UI", @"Web", @"Browser", @"View"] componentsJoinedByString:@""]); + wkMethod = class_getInstanceMethod(wkClass, @selector(inputAccessoryView)); + wkOriginalImp = method_getImplementation(wkMethod); + Class uiClass = NSClassFromString([@[@"WK", @"Content", @"View"] componentsJoinedByString:@""]); + uiMethod = class_getInstanceMethod(uiClass, @selector(inputAccessoryView)); + uiOriginalImp = method_getImplementation(uiMethod); + nilImp = imp_implementationWithBlock(^(id _s) { + return nil; + }); + //set defaults - // self.hideKeyboardAccessoryBar = YES; + self.hideKeyboardAccessoryBar = YES; self.disableScroll = NO; //self.styleDark = NO; - + + NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; + __weak IonicKeyboard* weakSelf = self; _keyboardShowObserver = [nc addObserverForName:UIKeyboardWillShowNotification object:nil queue:[NSOperationQueue mainQueue] @@ -42,6 +51,7 @@ - (void)pluginInitialize { [weakSelf.commandDelegate evalJs:@"cordova.fireWindowEvent('native.hidekeyboard'); "]; }]; } + - (BOOL)disableScroll { return _disableScroll; } @@ -62,24 +72,28 @@ - (void)setDisableScroll:(BOOL)disableScroll { _disableScroll = disableScroll; } +//keyboard swizzling inspired by: +//https://github.com/cjpearson/cordova-plugin-keyboard/ + +- (BOOL)hideKeyboardAccessoryBar { + return _hideKeyboardAccessoryBar; +} -// - (BOOL)hideKeyboardAccessoryBar { -// return _hideKeyboardAccessoryBar; -// } -// -// - (void)setHideKeyboardAccessoryBar:(BOOL)hideKeyboardAccessoryBar { -// if (hideKeyboardAccessoryBar == _hideKeyboardAccessoryBar || ![self.webView isKindOfClass:[UIWebView class]]) { -// return; -// } -// if (hideKeyboardAccessoryBar) { -// ((UIWebView*)self.webView).hackishlyHidesInputAccessoryView = YES; -// } -// else { -// ((UIWebView*)self.webView).hackishlyHidesInputAccessoryView = NO; -// } -// -// _hideKeyboardAccessoryBar = hideKeyboardAccessoryBar; -// } +- (void)setHideKeyboardAccessoryBar:(BOOL)hideKeyboardAccessoryBar { + if (hideKeyboardAccessoryBar == _hideKeyboardAccessoryBar) { + return; + } + + if (hideKeyboardAccessoryBar) { + method_setImplementation(wkMethod, nilImp); + method_setImplementation(uiMethod, nilImp); + } else { + method_setImplementation(wkMethod, wkOriginalImp); + method_setImplementation(uiMethod, uiOriginalImp); + } + + _hideKeyboardAccessoryBar = hideKeyboardAccessoryBar; +} /* - (BOOL)styleDark { @@ -129,15 +143,15 @@ - (void) disableScroll:(CDVInvokedUrlCommand*)command { } } -// - (void) hideKeyboardAccessoryBar:(CDVInvokedUrlCommand*)command { -// if (!command.arguments || ![command.arguments count]){ -// return; -// } -// id value = [command.arguments objectAtIndex:0]; -// if (value != [NSNull null]) { -// self.hideKeyboardAccessoryBar = [value boolValue]; -// } -// } +- (void) hideKeyboardAccessoryBar:(CDVInvokedUrlCommand*)command { + if (!command.arguments || ![command.arguments count]){ + return; + } + id value = [command.arguments objectAtIndex:0]; + if (value != [NSNull null]) { + self.hideKeyboardAccessoryBar = [value boolValue]; + } +} - (void) close:(CDVInvokedUrlCommand*)command { [self.webView endEditing:YES]; diff --git a/src/ios/UIWebViewExtension.h b/src/ios/UIWebViewExtension.h deleted file mode 100644 index 67555f5..0000000 --- a/src/ios/UIWebViewExtension.h +++ /dev/null @@ -1,4 +0,0 @@ -// @interface UIWebView (HackishAccessoryHiding) -// @property (nonatomic, assign) BOOL hackishlyHidesInputAccessoryView; -// //@property (nonatomic, assign) BOOL styleDark; -// @end diff --git a/src/ios/UIWebViewExtension.m b/src/ios/UIWebViewExtension.m deleted file mode 100644 index 75fde6c..0000000 --- a/src/ios/UIWebViewExtension.m +++ /dev/null @@ -1,109 +0,0 @@ -// #import -// #import -// #import "UIWebViewExtension.h" -// -// //Credit: https://gist.github.com/bjhomer/2048571 -// //Also: http://stackoverflow.com/a/23398487/1091751 -// @implementation UIWebView (HackishAccessoryHiding) -// -// static const char * const hackishFixClassName = "UIWebBrowserViewMinusAccessoryView"; -// static Class hackishFixClass = Nil; -// -// - (UIView *)hackishlyFoundBrowserView { -// UIScrollView *scrollView = self.scrollView; -// -// UIView *browserView = nil; -// for (UIView *subview in scrollView.subviews) { -// if ([NSStringFromClass([subview class]) hasPrefix:@"UIWebBrowserView"]) { -// browserView = subview; -// break; -// } -// } -// return browserView; -// } -// -// - (id)methodReturningNil { -// return nil; -// } -// -// - (void)ensureHackishSubclassExistsOfBrowserViewClass:(Class)browserViewClass { -// if (!hackishFixClass) { -// Class newClass = objc_allocateClassPair(browserViewClass, hackishFixClassName, 0); -// IMP nilImp = [self methodForSelector:@selector(methodReturningNil)]; -// class_addMethod(newClass, @selector(inputAccessoryView), nilImp, "@@:"); -// objc_registerClassPair(newClass); -// -// hackishFixClass = newClass; -// } -// } -// -// - (BOOL) hackishlyHidesInputAccessoryView { -// UIView *browserView = [self hackishlyFoundBrowserView]; -// return [browserView class] == hackishFixClass; -// } -// -// - (void) setHackishlyHidesInputAccessoryView:(BOOL)value { -// UIView *browserView = [self hackishlyFoundBrowserView]; -// if (browserView == nil) { -// return; -// } -// [self ensureHackishSubclassExistsOfBrowserViewClass:[browserView class]]; -// -// if (value) { -// object_setClass(browserView, hackishFixClass); -// } -// else { -// Class normalClass = objc_getClass("UIWebBrowserView"); -// object_setClass(browserView, normalClass); -// } -// [browserView reloadInputViews]; -// } -// /* ---------------------------------------------------------------- */ -// -// /* -// - (UIKeyboardAppearance) darkKeyboardAppearanceTemplateMethod { -// return UIKeyboardAppearanceDark; -// } -// -// - (UIKeyboardAppearance) lightKeyboardAppearanceTemplateMethod { -// return UIKeyboardAppearanceLight; -// } -// -// - (BOOL) styleDark { -// UIView *browserView = [self hackishlyFoundBrowserView]; -// if (browserView == nil) { -// return false; -// } -// -// Method m = class_getInstanceMethod( [self class], @selector( darkKeyboardAppearanceTemplateMethod ) ); -// IMP imp = method_getImplementation( m ); -// -// Method m2 = class_getInstanceMethod( [browserView class], @selector(keyboardAppearance) ); -// IMP imp2 = method_getImplementation( m2 ); -// -// return imp == imp2; -// } -// -// - (void) setStyleDark:(BOOL)styleDark { -// UIView *browserView = [self hackishlyFoundBrowserView]; -// if (browserView == nil) { -// return; -// } -// -// if ( styleDark ) { -// Method m = class_getInstanceMethod( [self class], @selector( darkKeyboardAppearanceTemplateMethod ) ); -// IMP imp = method_getImplementation( m ); -// const char* typeEncoding = method_getTypeEncoding( m ); -// class_replaceMethod( [browserView class], @selector(keyboardAppearance), imp, typeEncoding ); -// } -// else { -// Method m = class_getInstanceMethod( [self class], @selector( lightKeyboardAppearanceTemplateMethod ) ); -// IMP imp = method_getImplementation( m ); -// const char* typeEncoding = method_getTypeEncoding( m ); -// class_replaceMethod( [browserView class], @selector(keyboardAppearance), imp, typeEncoding ); -// } -// } -// */ -// -// @end -// diff --git a/www/ios/keyboard.js b/www/ios/keyboard.js index 07e4c90..888a18a 100644 --- a/www/ios/keyboard.js +++ b/www/ios/keyboard.js @@ -8,8 +8,7 @@ var Keyboard = function() { }; Keyboard.hideKeyboardAccessoryBar = function(hide) { - // exec(null, null, "Keyboard", "hideKeyboardAccessoryBar", [hide]); - console.warn('hideKeyboardAccessoryBar has been temporarily removed on iOS until an Apple-approved method is found.'); + exec(null, null, "Keyboard", "hideKeyboardAccessoryBar", [hide]); }; Keyboard.close = function() {