-
-
Save bjhomer/2048571 to your computer and use it in GitHub Desktop.
#import <objc/runtime.h> | |
#import <UIKit/UIKit.h> | |
@interface UIWebView (HackishAccessoryHiding) | |
@property (nonatomic, assign) BOOL hackishlyHidesInputAccessoryView; | |
@end | |
@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) { | |
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]; | |
} | |
@end |
#import <objc/runtime.h> | |
#import <UIKit/UIKit.h> | |
@interface UIWebView (HackishAccessoryHiding) | |
@property (nonatomic, assign) BOOL hackishlyHidesInputAccessoryView; | |
@end | |
@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]; | |
} | |
@end |
Hmm. So I do. Not sure how that happened. Will fix.
Fixed. Thanks for pointing that out.
Just an fyi, this gist crashes in iOS 4.2 because there is no .scrollView property on WebView. I plugged that hole in a super-hacky by replacing hackishlyFoundBrowserView with the following:
-
(UIView *)hackishlyFoundBrowserView {
UIScrollView *scrollView;if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 5.0) {
scrollView = self.scrollView;
} else {
scrollView = [self.subviews lastObject]; // iOS 2.x (?) - 4.x
}UIView *browserView = nil;
for (UIView *subview in scrollView.subviews) {
if ([NSStringFromClass([subview class]) hasPrefix:@"UIWebBrowserView"]) {
browserView = subview;
break;
}
}
return browserView;
}
For anyone who wants to actually use this in your code, you need to do three things:
-
Make setHackishlyHidesInputAccessoryView available to your code (change the interface to):
@interface UIWebView (HackishAccessoryHiding)
@Property (nonatomic, assign) BOOL hackishlyHidesInputAccessoryView;- (void) setHackishlyHidesInputAccessoryView:(BOOL)value;
@EnD
- (void) setHackishlyHidesInputAccessoryView:(BOOL)value;
-
Call that in your code somewhere. For me, I called it after initiating load of a webview as follows:
[self.webView setHackishlyHidesInputAccessoryView:YES]; -
In ensureHackishSubclassExistsOfBrowserViewClass, add 'Class' before the first appearance of the 'newClass' variable. It should now look like:
Class newClass = objc_allocateClassPair(browserViewClass, hackishFixClassName, 0);
And also follow the advice above if you want it to work on iOS 4.x
- There's no need to declare the
-setHackishlyHidesInputAccessoryView:
method; it's implicitly declared as part of the@property
. That's how@property
s work. You can read more about it here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html - Correct; you just need to call
webView.hackishlyHidesInputAccessoryView = YES
or[webView setHackishlyHidesInputAccessoryView:YES]
. Both are equivalent. - Oh, you're absolutely right, I somehow missed the variable type declaration there. I'll fix that.
Do you think this will pass AppStore review?
Based on Mike Ash comments http://www.mikeash.com/pyblog/friday-qa-2010-11-6-creating-classes-at-runtime-in-objective-c.html, would it be better to do the following?
Method inputAccessoryView = class_getInstanceMethod([NSResponder class],
@selector(inputAccessoryView));
const char *types = method_getTypeEncoding(inputAccessoryView);
class_addMethod(newClass, @selector(inputAccessoryView), nilImp, types);
@devinfoley
I think some apps like Evernote, which is probably using a UIWebView to show/edit notes, may use something similar to this Gist.
I used a variation of this to get rid of the inputView entirely, nice work.
I seems like does not work on IOS 6.x.
how to get rid of inputView?
@bjhomer
I'm afraid I'm very inexperienced within Objective-C, I've created a .m file (UIWebViewFormAssistantHack.m) with this contents, and although you explained that the property should be accessible, I can't seem to reference it from the viewDidLoad scope using self.webView.sethackishlyHidesInputAccessoryView(YES); (or self.webView.hackishlyHidesInputAccessoryView = YES for that matter)
Would you be so kind to help me clarify what I am doing wrong?
@zhangchuqi
Just replace references to "inputAccessoryView" with "inputView" in the above code. And instead of returning nil (which I think in the case of inputView will still cause the default keyboard to show) you'll want to return an empty UIView.
@AskeG
You probably know this by now, but you need to include the category's .h file in header of your view controller.
I think this is way much better than https://github.com/don/KeyboardToolbarRemover
You can eliminate [browserView reloadInputViews];
I didn't see a license on the website. Would you be willing to license the code under the MIT (http://opensource.org/licenses/MIT) or BSD (http://opensource.org/licenses/BSD-2-Clause) license?
hi ! can we custom inputView for UIWebview with this solutions?
I've made a Cocoapod based on your solution.
I also posted a gist that allows to customize OR disable the inputAccessoryView:
https://gist.github.com/kgaidis/5f9a8c7063b687cc3946fad6379c1a66
how do i do this for wkwebview
@tinhbka
replace UIWebView's inputView :
-(id)methodReturningKeyboard {
return [[JSInputView alloc] initWithFrame:CGRectZero];
}
- (void)ensureHackishSubclassExistsOfBrowserViewClass:(Class)browserViewClass {
if (!hackishFixClass) {
Class newClass = objc_allocateClassPair(browserViewClass, hackishFixClassName, 0);
newClass = objc_allocateClassPair(browserViewClass, hackishFixClassName, 0);
IMP keyboardImp = [self methodForSelector:@selector(methodReturningKeyboard)];
class_addMethod(newClass, @selector(inputView), keyboardImp, "@@:");
objc_registerClassPair(newClass);
hackishFixClass = newClass;
}
}
You have two
.m
files with almost identical content.