Removing WKWebView Accesory bar in Swift
For those who are still looking, the WebKit team updated WKWebView
(iOS 13+) so that you can subclass it to remove/update the input accessory view:
https://trac.webkit.org/changeset/246229/webkit#file1
In Swift, I subclassed it, and returned nil. Worked as expected. I hope it helps.
FYI: I checked the docs, and it doesn't mention not to subclass WKWebView
, so subclassing is allowed.
import WebKit
class RichEditorWebView: WKWebView {
var accessoryView: UIView?
override var inputAccessoryView: UIView? {
// remove/replace the default accessory view
return accessoryView
}
}
You can find a working version of it here: https://github.com/cbess/RichEditorView/commits/master
Hiding Keyboard accessorybar in WKWebView
This is possible in WKWebView
with a variant of the method for swizzling out the inputAccessoryView
on UIWebView
.
First, add this little class:
@interface _NoInputAccessoryView : NSObject
@end
@implementation _NoInputAccessoryView
- (id)inputAccessoryView {
return nil;
}
@end
Next, add this method:
- (void)removeInputAccessoryViewFromWKWebView:(WKWebView *)webView {
UIView *targetView;
for (UIView *view in webView.scrollView.subviews) {
if([[view.class description] hasPrefix:@"WKContent"]) {
targetView = view;
}
}
if (!targetView) {
return;
}
NSString *noInputAccessoryViewClassName = [NSString stringWithFormat:@"%@_NoInputAccessoryView", targetView.class.superclass];
Class newClass = NSClassFromString(noInputAccessoryViewClassName);
if(newClass == nil) {
newClass = objc_allocateClassPair(targetView.class, [noInputAccessoryViewClassName cStringUsingEncoding:NSASCIIStringEncoding], 0);
if(!newClass) {
return;
}
Method method = class_getInstanceMethod([_NoInputAccessoryView class], @selector(inputAccessoryView));
class_addMethod(newClass, @selector(inputAccessoryView), method_getImplementation(method), method_getTypeEncoding(method));
objc_registerClassPair(newClass);
}
object_setClass(targetView, newClass);
}
Then all you have to do is call that method and pass in your WKWebView
:
[self removeInputAccessoryViewFromWKWebView:webView];
Note: I do not yet know for sure if this will pass app review, but it is extremely similar to the same code I used for UIWebView
, and that did pass review.
Update: This code is in an app that has passed App Store review.
Modifying keyboard toolbar / accessory view with WKWebView
Maybe this tutorial on UITextInputAssistantItem
would be helpful.
That said, after fiddling around with this for a while, using WKWebView
I still could only get this to work for the first time the keyboard displayed, and every time after that it would return to its original state. The only thing I found that consistently worked was something like the following:
class ViewController: UIViewController {
@IBOutlet weak private var webView: WKWebView!
private var contentView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
webView.loadHTMLString("<html><body><div contenteditable='true'></div></body></html>", baseURL: nil)
for subview in webView.scrollView.subviews {
if subview.classForCoder.description() == "WKContentView" {
contentView = subview
}
}
inputAssistantItem.leadingBarButtonGroups = [UIBarButtonItemGroup(barButtonItems: [UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(donePressed(_:)))], representativeItem: nil)]
inputAssistantItem.trailingBarButtonGroups = [UIBarButtonItemGroup(barButtonItems: [UIBarButtonItem(barButtonSystemItem: .camera, target: self, action: #selector(openCamera(_:))), UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(actionPressed(_:)))], representativeItem: nil)]
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow), name: .UIKeyboardDidShow, object: nil)
}
@objc private func donePressed(_ sender: UIBarButtonItem) {
view.endEditing(true)
}
@objc private func openCamera(_ sender: UIBarButtonItem) {
print("camera pressed")
}
@objc private func actionPressed(_ sender: UIBarButtonItem) {
print("action pressed")
}
@objc private func keyboardDidShow() {
contentView?.inputAssistantItem.leadingBarButtonGroups = [UIBarButtonItemGroup(barButtonItems: [UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(donePressed(_:)))], representativeItem: nil)]
contentView?.inputAssistantItem.trailingBarButtonGroups = [UIBarButtonItemGroup(barButtonItems: [UIBarButtonItem(barButtonSystemItem: .camera, target: self, action: #selector(openCamera(_:))), UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(actionPressed(_:)))], representativeItem: nil)]
}
}
It will give something like this:
It's a bit frustrating to have to set this on the content view every time the keyboard shows, as well as on the view controller itself, and I hope there's a better way to do this.... But unfortunately I could not find it.
How to edit accessory view of keyboard shown from WKWebView?
Found a way, ended up to swizzle UIToolbars. Hopefully everything is there, but you would get an idea. Swift 4:
class YourController: UIViewController {
@IBOutlet weak var webView: PWebView!
var toolbar : UIToolbar?
func viewDidLoad() {
webView.addInputAccessoryView(toolbar: self.getToolbar(height: 44))
}
func getToolbar(height: Int) -> UIToolbar? {
let toolBar = UIToolbar()
toolBar.frame = CGRect(x: 0, y: 50, width: 320, height: height)
toolBar.barStyle = .black
toolBar.tintColor = .white
toolBar.barTintColor = UIColor.blue
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(onToolbarDoneClick(sender:)) )
let flexibleSpaceItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil )
toolBar.setItems([flexibleSpaceItem, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
toolBar.sizeToFit()
return toolBar
}
@objc func onToolbarDoneClick(sender: UIBarButtonItem) {
webView?.resignFirstResponder()
}
}
var ToolbarHandle: UInt8 = 0
extension WKWebView {
func addInputAccessoryView(toolbar: UIView?) {
guard let toolbar = toolbar else {return}
objc_setAssociatedObject(self, &ToolbarHandle, toolbar, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
var candidateView: UIView? = nil
for view in self.scrollView.subviews {
let description : String = String(describing: type(of: view))
if description.hasPrefix("WKContent") {
candidateView = view
break
}
}
guard let targetView = candidateView else {return}
let newClass: AnyClass? = classWithCustomAccessoryView(targetView: targetView)
guard let targetNewClass = newClass else {return}
object_setClass(targetView, targetNewClass)
}
func classWithCustomAccessoryView(targetView: UIView) -> AnyClass? {
guard let _ = targetView.superclass else {return nil}
let customInputAccesoryViewClassName = "_CustomInputAccessoryView"
var newClass: AnyClass? = NSClassFromString(customInputAccesoryViewClassName)
if newClass == nil {
newClass = objc_allocateClassPair(object_getClass(targetView), customInputAccesoryViewClassName, 0)
} else {
return newClass
}
let newMethod = class_getInstanceMethod(WKWebView.self, #selector(WKWebView.getCustomInputAccessoryView))
class_addMethod(newClass.self, #selector(getter: WKWebView.inputAccessoryView), method_getImplementation(newMethod!), method_getTypeEncoding(newMethod!))
objc_registerClassPair(newClass!)
return newClass
}
@objc func getCustomInputAccessoryView() -> UIView? {
var superWebView: UIView? = self
while (superWebView != nil) && !(superWebView is WKWebView) {
superWebView = superWebView?.superview
}
guard let webView = superWebView else {return nil}
let customInputAccessory = objc_getAssociatedObject(webView, &ToolbarHandle)
return customInputAccessory as? UIView
}
}
Hide shortcut keyboard bar for UIWebView in iOS 9
Using method swiziling we can remove the keyboard shortcut bar (only works with ObjC).
- (void)hideKeyboardShortcutBar
{
Class webBrowserClass = NSClassFromString(@"UIWebBrowserView");
Method method = class_getInstanceMethod(webBrowserClass, @selector(inputAccessoryView));
IMP newImp = imp_implementationWithBlock(^(id _s) {
if ([self.webView respondsToSelector:@selector(inputAssistantItem)]) {
UITextInputAssistantItem *inputAssistantItem = [self.webView inputAssistantItem];
inputAssistantItem.leadingBarButtonGroups = @[];
inputAssistantItem.trailingBarButtonGroups = @[];
}
return nil;
});
method_setImplementation(method, newImp);
}
inputAccessoryView
: This property is typically used to attach an accessory view to the
system-supplied keyboard that is presented for UITextField and
UITextView objects.
So the new implementation block will be fired every time the keyboard pops up.
UPDATE
To remove the accessory view from WKWebView
use WKContentView
instead of UIWebBrowserView
Related Topics
Setting Up a Plist to Store Application Data (Not Settings) for an iPhone Game
Swift: Gradient Along a Bezier Path (Using Calayers)
Apple MACh-O Linker (Id) Warning:Building for MACosx, But Linking Against Dylib Built for iOS
Detect When a Unicode Character Cannot Be Displayed Correctly
What Does It Mean for Something to Be Thread Safe in iOS
Native Zlib Inflate/Deflate for Swift3 on iOS
iOS Are Methods Called by Delegates and Observers Executed on the Main Thread
Enforce Collectionview to Have Only 2 Rows
Why Is Audio Coming Up Garbled When Using Avassetreader with Audio Queue
Universal Links Not Working on iOS10
Ios7 - Device Unique Identifier
Visually Modifying a Uitoolbar from Xcode Storyboard
How to Animate Changing a Uilabel's Textalignment
How to Control My Headset for My Music Player
Ios9: Alternative to Uidevice.Currentdevice().Setvalue(...) to Force Orientation