Hiding the inputAccessoryView of a UIWebView

UIWebView is a workhorse of a view. You can use it to display web pages, of course, but you can also use it to display locally-generated rich content.

I was recently using UIWebView and the contentEditable attribute to implement a text field that allows inline images, etc. The fact that I’m using a web view, though, is an implementation detail; to the user, it should just look like any other text field.

Unfortunately, UIWebView loves to show that little “Previous/Next” bar above the keyboard. It was useless and occupying valuable screen space, so I determined to find a way around it.

This bar is the enemy

It was actually fairly simple; a UIWebView contains a UIWebBrowserView, a private class that does most of the real work. I discovered via class-dump that it also implements -(id)inputAccessoryView, returning the bar in question. So at runtime, I create a subclass of UIWebBrowserView and override that method to return nil. Then I call [self reloadInputViews], and I’m done.

I’ve wrapped this up into a UIWebView category, which exposes a single property:
@property (nonatomic, assign) BOOL hackishlyHidesInputAccessoryView;
You can find it on gist.

Enjoy.

Note: This code has only been tested on iOS 5.1, and may not work on other versions of iOS. I’ve filed rdar://11040111 requesting a supported way to hide this bar. For now, though, this works, and it should fail gracefully if the internal UIWebView view hierarchy changes. If Apple ever releases a supported method for hiding this bar, please use that instead. I only resort to hackery like this when I can find no other option.

11 responses to “Hiding the inputAccessoryView of a UIWebView”

  1. That's just a bad idea. Best case: at some point in the future this code will just stop working with no warning (assuming you're very very careful with the implementation), worst case: strange crashes and/or Apple rejects your app from the store.You're messing with private APIs here. Don't do that.

    Like

  2. Acknowledged; this is messing with private view hierarchies and is liable to break in even bugfix releases. But the fact remains that there's no supported way to remove this bar, and I needed to remove it. That means unsupported API. I spent a week looking for a non-private API workaround, including some really ugly contortions. No go.The implementation I provided should fail gracefully if the internal view hierarchy changes. I haven't tested it against the app store validation yet, but it contains no references to private symbols so I suspect it will pass. It's possible it could cause a crash, but I've played with it quite a bit, and it seems stable in iOS 5.1.I don't love the idea of messing with private implementation details like this, but until there's a better way, this is at least a better workaround than some of the other stuff I've seen posted online.

    Like

  3. it crashed in iOS 4.3,- (UIView *)hackishlyFoundBrowserView { UIScrollView *scrollView = self.scrollView; //<<<Crashing line…..}Did you ever try with earlier version of iOS?

    Like

  4. Note my disclaimer at the bottom of the post; this has only been tested on iOS 5.1. That said, I'm not surprised the code above doesn't work on iOS 4.3, since the “scrollView” method was only introduced on UIWebView in iOS 5.0.It shouldn't be too hard to work around that particular issue, though; just wander through The web view's subviews until you find a UIScrollView. I can't guarantee the rest of this will work on 4.3, but that particular issue shouldn't be the roadblock.

    Like

  5. I found this to be better solution for phonegap apps:http://stackoverflow.com/a/9276023/35364It also doesn't use any private apis (from the looks of it). It just looks for the “view” that is the black bar and removes it every time the keyboard is about to pop up.

    Like

  6. Would you mind explaining how to use the gist you posted in your Xcode project? It seems like setHackishlyHidesInputAccessoryView is where the magic happens, but it's not called from anywhere, including from the only method I can call from my code.Am I supposed to make an interface to setHackishlyHidesInputAccessoryView and call it from somewhere? Sorry if it's a dumb question, just not obvious to me.

    Like

  7. Hi,Very interesting code… Have you find a way to set focus to conteteditable without touch from users? In ios 6 there is a property that allows to do that, but in ios 5 i can't find a way…

    Like

  8. No, there's no way I've found pre-iOS6 to programmatically give focus to a contentEditable div. Sorry.

    Like

  9. There is a way to remove the accessory view without messing with private frameworks.This is not my solution. I found this while researching a bit to build a rich text editor. This solution works wonderfully though.The “bonus” part of this tutorial talks about removing the bar. I can confirm it works beautifully on iOS 6.http://ios-blog.co.uk/tutorials/rich-text-editing-a-simple-start-part-1/

    Like

  10. The problem with solutions that just dig around in the keyboard window hierarchy is that it doesn't actually change the layout size of the keyboard as reported in the “keyboard will show/hide” notifications. It can also cause the window to, in some cases, still intercept taps in the area where the bar used to be. Those were the problems that led me to look for this solution in the first place.

    Like

  11. After fiddling around for a while I come to the conclusion you're right. I'm having problems setting my “custom” inputAccessoryView programatically, since UIWebView's is readonly.Hopefully Apple will provide a better way to get rid of this bar eventually. Sadly I don't want to design this component with a private API because of all the consequences it will have.In the meantime, it looks like I will have no other option but to place my own bar programatically using “magical numbers”, but that's just not future proof.

    Like

Leave a comment