Reputation: 27123
I have to remove this bar as here link but for iOS 7 this code does not work.
Upvotes: 13
Views: 7275
Reputation: 57040
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!)
}
}
Upvotes: 25
Reputation: 1053
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.
Upvotes: 1
Reputation: 15579
I created a gist to accomplish this: https://gist.github.com/kgaidis/5f9a8c7063b687cc3946fad6379c1a66
It's a UIWebView
category where all you do is change the customInputAccessoryView
property:
@interface UIWebView (CustomInputAccessoryView)
@property (strong, nonatomic) UIView *customInputAccessoryView;
@end
You can either set it to nil
to remove it or you can set a new view on it to change it.
Keep in mind, this also uses private API's, so use at your own risk, but it seems like a lot of apps do similar things nonetheless.
Upvotes: 0
Reputation: 3681
I've came across this awesome solution, but I needed to get the inputAccessoryView back as well. I added this method:
- (void)__addInputAccessoryView {
UIView* subview;
for (UIView* view in self.scrollView.subviews) {
if([[view.class description] hasSuffix:@"SwizzleHelper"])
subview = view;
}
if(subview == nil) return;
Class newClass = subview.superclass;
object_setClass(subview, newClass);
}
It does seem to work as intended with no side effects, but I can't get rid of the feeling that my pants are on fire.
If you want Leo Natan's solution to work with WKWebView instead of UIWebView just change prefix from "UIWeb" to "WKContent".
Upvotes: 0