Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modifying keyboard toolbar / accessory view with WKWebView

I'm using a WKWebView with a content-editable div as the core of a rich-text editor, and would like to modify the top toolbar above the keyboard—the one that contains the autocorrect suggestions and formatting buttons. (Not sure if this counts as an input accessory view or not).

I've found a few posts showing how to remove the bar, but none of them seems to work, and ideally I'd like to keep the autocorrect part anyway.

At least one app, Ulysses, does this (though I don't know if it's with a web view):

Ulysses

And indeed, I'm pretty sure I can achieve it by doing surgery on the keyboard view hierarchy...but that seems like a tedious and brittle approach.

Is there a better way?

Thanks!

like image 601
Dave Feldman Avatar asked Feb 25 '18 20:02

Dave Feldman


Video Answer


1 Answers

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:

enter image description here

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.

like image 137
Samantha Avatar answered Nov 14 '22 21:11

Samantha