Skip to content

@bjhomer /UIWebView+AccessoryHiding.m
Created

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
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

You have two .m files with almost identical content.

@bjhomer
Owner

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

@bjhomer
Owner

Fixed. Thanks for pointing that out.

@ronadams

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

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
Owner

@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

Do you think this will pass AppStore review?

@julientherier

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

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

@waterzhang0423

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

@waterzhang0423

@matt-curtis:

how to get rid of inputView?

@AskeG

@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

@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

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

@iospro

You can eliminate [browserView reloadInputViews];

@Schandlich

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.