Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

This comment has been minimized.

Show comment
Hide comment
@0xced

0xced Mar 20, 2012

You have two .m files with almost identical content.

0xced commented Mar 20, 2012

You have two .m files with almost identical content.

@bjhomer

This comment has been minimized.

Show comment
Hide comment
@bjhomer

bjhomer Mar 20, 2012

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

Owner

bjhomer commented Mar 20, 2012

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

@bjhomer

This comment has been minimized.

Show comment
Hide comment
@bjhomer

bjhomer Mar 20, 2012

Fixed. Thanks for pointing that out.

Owner

bjhomer commented Mar 20, 2012

Fixed. Thanks for pointing that out.

@ronadams

This comment has been minimized.

Show comment
Hide comment
@ronadams

ronadams Sep 10, 2012

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

ronadams commented Sep 10, 2012

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

This comment has been minimized.

Show comment
Hide comment
@jessep

jessep 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

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

This comment has been minimized.

Show comment
Hide comment
@bjhomer

bjhomer 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.
Owner

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

This comment has been minimized.

Show comment
Hide comment
@devinfoley

devinfoley Oct 23, 2012

Do you think this will pass AppStore review?

devinfoley commented Oct 23, 2012

Do you think this will pass AppStore review?

@julientherier

This comment has been minimized.

Show comment
Hide comment
@julientherier

julientherier May 22, 2013

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.

julientherier commented May 22, 2013

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

This comment has been minimized.

Show comment
Hide comment
@matt-curtis

matt-curtis Jun 24, 2013

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

matt-curtis commented Jun 24, 2013

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

@waterzhang0423

This comment has been minimized.

Show comment
Hide comment
@waterzhang0423

waterzhang0423 Jul 8, 2013

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

waterzhang0423 commented Jul 8, 2013

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

@waterzhang0423

This comment has been minimized.

Show comment
Hide comment
@waterzhang0423

waterzhang0423 Jul 8, 2013

@matt-curtis:

how to get rid of inputView?

waterzhang0423 commented Jul 8, 2013

@matt-curtis:

how to get rid of inputView?

@AskeG

This comment has been minimized.

Show comment
Hide comment
@AskeG

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

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

This comment has been minimized.

Show comment
Hide comment
@matt-curtis

matt-curtis Sep 11, 2013

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

matt-curtis commented Sep 11, 2013

@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

This comment has been minimized.

Show comment
Hide comment
@drunkhacker

drunkhacker commented Nov 19, 2013

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

@iospro

This comment has been minimized.

Show comment
Hide comment
@iospro

iospro Jan 26, 2014

You can eliminate [browserView reloadInputViews];

iospro commented Jan 26, 2014

You can eliminate [browserView reloadInputViews];

@Schandlich

This comment has been minimized.

Show comment
Hide comment
@Schandlich

Schandlich Dec 16, 2014

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?

Schandlich commented Dec 16, 2014

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

This comment has been minimized.

Show comment
Hide comment
@tinhbka

tinhbka Jul 15, 2015

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

tinhbka commented Jul 15, 2015

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

@lauracpierre

This comment has been minimized.

Show comment
Hide comment
@lauracpierre

lauracpierre commented Dec 9, 2015

I've made a Cocoapod based on your solution.

https://cocoapods.org/pods/FA_InputAccessoryViewWebView

@kgaidis

This comment has been minimized.

Show comment
Hide comment
@kgaidis

kgaidis May 5, 2016

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

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

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

This comment has been minimized.

Show comment
Hide comment
@kapilrathore

kapilrathore May 30, 2016

how do i do this for wkwebview

kapilrathore commented May 30, 2016

how do i do this for wkwebview

@tomfriwel

This comment has been minimized.

Show comment
Hide comment
@tomfriwel

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

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