Skip to content

Instantly share code, notes, and snippets.

@bjhomer
Created March 16, 2012 05:03
Show Gist options
  • Save bjhomer/2048571 to your computer and use it in GitHub Desktop.
Save bjhomer/2048571 to your computer and use it in GitHub Desktop.
Hiding the inputAccessoryView of a UIWebView
#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
@0xced
Copy link

0xced commented Mar 20, 2012

You have two .m files with almost identical content.

@bjhomer
Copy link
Author

bjhomer commented Mar 20, 2012

Hmm. So I do. Not sure how that happened. Will fix.

@bjhomer
Copy link
Author

bjhomer commented Mar 20, 2012

Fixed. Thanks for pointing that out.

@ronadams
Copy link

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;
    }

@jessep
Copy link

jessep commented Sep 26, 2012

For anyone who wants to actually use this in your code, you need to do three things:

  1. Make setHackishlyHidesInputAccessoryView available to your code (change the interface to):
    @interface UIWebView (HackishAccessoryHiding)
    @Property (nonatomic, assign) BOOL hackishlyHidesInputAccessoryView;

    • (void) setHackishlyHidesInputAccessoryView:(BOOL)value;
      @EnD
  2. Call that in your code somewhere. For me, I called it after initiating load of a webview as follows:
    [self.webView setHackishlyHidesInputAccessoryView:YES];

  3. 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

@bjhomer
Copy link
Author

bjhomer commented Sep 26, 2012

@jessep:

  1. There's no need to declare the -setHackishlyHidesInputAccessoryView: method; it's implicitly declared as part of the @property. That's how @propertys work. You can read more about it here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html
  2. Correct; you just need to call webView.hackishlyHidesInputAccessoryView = YES or [webView setHackishlyHidesInputAccessoryView:YES]. Both are equivalent.
  3. Oh, you're absolutely right, I somehow missed the variable type declaration there. I'll fix that.

@devinfoley
Copy link

Do you think this will pass AppStore review?

@julientherier
Copy link

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.

@matt-curtis
Copy link

I used a variation of this to get rid of the inputView entirely, nice work.

@waterzhang0423
Copy link

I seems like does not work on IOS 6.x.

@waterzhang0423
Copy link

@matt-curtis:

how to get rid of inputView?

@AskeG
Copy link

AskeG commented Aug 2, 2013

@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?

@matt-curtis
Copy link

@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.

@drunkhacker
Copy link

I think this is way much better than https://github.com/don/KeyboardToolbarRemover

@iospro
Copy link

iospro commented Jan 26, 2014

You can eliminate [browserView reloadInputViews];

@Schandlich
Copy link

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?

@tinhbka
Copy link

tinhbka commented Jul 15, 2015

hi ! can we custom inputView for UIWebview with this solutions?

@lauracpierre
Copy link

I've made a Cocoapod based on your solution.

https://cocoapods.org/pods/FA_InputAccessoryViewWebView

@kgaidis
Copy link

kgaidis commented May 5, 2016

I also posted a gist that allows to customize OR disable the inputAccessoryView:

https://gist.github.com/kgaidis/5f9a8c7063b687cc3946fad6379c1a66

@kapilrathore
Copy link

how do i do this for wkwebview

@tomfriwel
Copy link

tomfriwel commented Nov 25, 2016

@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;
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment