iOS 7 UIWebView keyboard issue
Solution 1:
We remove this bar with some Objective C runtime trickery.
We have a class which has one method:
@interface _SwizzleHelper : NSObject @end
@implementation _SwizzleHelper
-(id)inputAccessoryView
{
return nil;
}
@end
Once we have a web view which we want to remove the bar from, we iterate its scroll view's subviews and take the UIWebDocumentView
class. We then dynamically make the superclass of the class we created above to be the subview's class (UIWebDocumentView - but we cannot say that upfront because this is private API), and replace the subview's class to our class.
#import "objc/runtime.h"
-(void)__removeInputAccessoryView
{
UIView* subview;
for (UIView* view in self.scrollView.subviews) {
if([[view.class description] hasPrefix:@"UIWeb"])
subview = view;
}
if(subview == nil) return;
NSString* name = [NSString stringWithFormat:@"%@_SwizzleHelper", subview.class.superclass];
Class newClass = NSClassFromString(name);
if(newClass == nil)
{
newClass = objc_allocateClassPair(subview.class, [name cStringUsingEncoding:NSASCIIStringEncoding], 0);
if(!newClass) return;
Method method = class_getInstanceMethod([_SwizzleHelper class], @selector(inputAccessoryView));
class_addMethod(newClass, @selector(inputAccessoryView), method_getImplementation(method), method_getTypeEncoding(method));
objc_registerClassPair(newClass);
}
object_setClass(subview, newClass);
}
The equivalent of the above in Swift 3.0:
import UIKit
import ObjectiveC
var swizzledClassMapping = [AnyClass]()
extension UIWebView {
func noInputAccessoryView() -> UIView? {
return nil
}
public func removeInputAccessoryView() {
var subview: AnyObject?
for (_, view) in scrollView.subviews.enumerated() {
if NSStringFromClass(type(of: view)).hasPrefix("UIWeb") {
subview = view
}
}
guard subview != nil else {
return
}
//Guard in case this method is called twice on the same webview.
guard !(swizzledClassMapping as NSArray).contains(type(of: subview!)) else {
return;
}
let className = "\type(of: subview!)_SwizzleHelper"
var newClass : AnyClass? = NSClassFromString(className)
if newClass == nil {
newClass = objc_allocateClassPair(type(of: subview!), className, 0)
guard newClass != nil else {
return;
}
let method = class_getInstanceMethod(type(of: self), #selector(UIWebView.noInputAccessoryView))
class_addMethod(newClass!, #selector(getter: UIResponder.inputAccessoryView), method_getImplementation(method), method_getTypeEncoding(method))
objc_registerClassPair(newClass!)
swizzledClassMapping += [newClass!]
}
object_setClass(subview!, newClass!)
}
}
Solution 2:
I've made a cocoapod based on this blog post from @bjhomer.
You can replace the inputaccessoryview and not just hide it. I hope this will help people with the same issue.
https://github.com/lauracpierre/FA_InputAccessoryViewWebView
You can find the cocoapod page right here.