You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改WKWebView富文本编辑器的键盘上方工具栏?

Customizing the WKWebView Editing Toolbar (Keep Autocorrect, Replace Formatting Buttons)

First off, I feel your pain—WKWebView’s built-in editing toolbar for contenteditable elements is notoriously tricky to modify without sacrificing features like autocorrect. Let’s break down the options you have, including the most reliable approaches that keep autocorrect intact.

Why Removing the Default Toolbar Isn’t Working

The toolbar you’re seeing is WebKit’s native editing UI, tightly coupled to the contenteditable div’s text input handling. Standard tricks like setting webView.inputAccessoryView = nil won’t work because WebKit overrides this when editing starts. CSS can’t touch it either, since it’s a native system view, not part of the web content.

Approach 1: Hybrid Native-Web Toolbar (App Store Safe, Keeps Autocorrect)

If you’re willing to place your formatting buttons in a separate toolbar at the top of your screen (instead of attached to the keyboard), this is the most reliable, App Store-compliant method. Here’s how to set it up:

Step 1: Add a Native Toolbar to Your View Controller

class RichTextEditorViewController: UIViewController {
    private var webView: WKWebView!
    private var formattingToolbar: UIToolbar!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Initialize WKWebView
        let config = WKWebViewConfiguration()
        webView = WKWebView(frame: view.bounds, configuration: config)
        webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(webView)
        
        // Load your contenteditable HTML
        let html = """
        <div contenteditable="true" autocorrect="on" autocapitalize="sentences">Type here...</div>
        """
        webView.loadHTMLString(html, baseURL: nil)
        
        // Set up formatting toolbar
        setupFormattingToolbar()
    }
    
    private func setupFormattingToolbar() {
        formattingToolbar = UIToolbar(frame: CGRect(x: 0, y: view.safeAreaInsets.top, width: view.bounds.width, height: 44))
        formattingToolbar.barStyle = .default
        
        // Create formatting buttons
        let boldButton = UIBarButtonItem(title: "B", style: .plain, target: self, action: #selector(toggleBold))
        let italicButton = UIBarButtonItem(title: "I", style: .plain, target: self, action: #selector(toggleItalic))
        let underlineButton = UIBarButtonItem(title: "U", style: .plain, target: self, action: #selector(toggleUnderline))
        let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        
        formattingToolbar.items = [boldButton, italicButton, underlineButton, flexibleSpace]
        view.addSubview(formattingToolbar)
        
        // Adjust webView frame to make space for the toolbar
        webView.frame = CGRect(
            x: 0,
            y: formattingToolbar.frame.height + view.safeAreaInsets.top,
            width: view.bounds.width,
            height: view.bounds.height - formattingToolbar.frame.height - view.safeAreaInsets.top - view.safeAreaInsets.bottom
        )
    }
    
    // MARK: - Formatting Actions
    @objc private func toggleBold() {
        webView.evaluateJavaScript("document.execCommand('bold', false, null);")
    }
    
    @objc private func toggleItalic() {
        webView.evaluateJavaScript("document.execCommand('italic', false, null);")
    }
    
    @objc private func toggleUnderline() {
        webView.evaluateJavaScript("document.execCommand('underline', false, null);")
    }
}

Pros:

  • Fully App Store compliant (no private API usage)
  • Retains the system’s autocorrect suggestions in the keyboard toolbar
  • Full control over your formatting buttons
  • Works across all iOS versions

Cons:

  • Formatting buttons aren’t attached to the keyboard (some users might prefer them there)

Approach 2: Modify the Native Input Accessory View (Fragile, But Closer to Ulysses)

If you absolutely need your formatting buttons in the keyboard’s accessory view, you can traverse WKWebView’s view hierarchy to modify the existing toolbar. This is fragile (Apple can change the view structure in iOS updates) but works for many cases.

Step 1: Listen for Keyboard Events and Modify the Toolbar

class RichTextEditorViewController: UIViewController {
    private var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Initialize WKWebView and load content (same as Approach 1)
        let config = WKWebViewConfiguration()
        webView = WKWebView(frame: view.bounds, configuration: config)
        webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(webView)
        
        let html = """
        <div contenteditable="true" autocorrect="on" autocapitalize="sentences">Type here...</div>
        """
        webView.loadHTMLString(html, baseURL: nil)
        
        // Listen for keyboard show events
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(keyboardDidShow),
            name: UIResponder.keyboardDidShowNotification,
            object: nil
        )
    }
    
    @objc private func keyboardDidShow(notification: NSNotification) {
        // Traverse the view hierarchy to find the input accessory toolbar
        func findInputToolbar(in view: UIView) -> UIToolbar? {
            if let toolbar = view as? UIToolbar, 
               toolbar.superview?.superview is UIInputView {
                return toolbar
            }
            for subview in view.subviews {
                if let toolbar = findInputToolbar(in: subview) {
                    return toolbar
                }
            }
            return nil
        }
        
        guard let toolbar = findInputToolbar(in: webView) else { return }
        
        // Remove default formatting buttons (adjust based on iOS version)
        toolbar.items?.removeAll()
        
        // Add your custom buttons
        let boldButton = UIBarButtonItem(title: "B", style: .plain, target: self, action: #selector(toggleBold))
        let italicButton = UIBarButtonItem(title: "I", style: .plain, target: self, action: #selector(toggleItalic))
        let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(endEditing))
        let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        
        toolbar.items = [boldButton, italicButton, flexibleSpace, doneButton]
        toolbar.setNeedsLayout()
    }
    
    // MARK: - Actions
    @objc private func toggleBold() {
        webView.evaluateJavaScript("document.execCommand('bold', false, null);")
    }
    
    @objc private func toggleItalic() {
        webView.evaluateJavaScript("document.execCommand('italic', false, null);")
    }
    
    @objc private func endEditing() {
        webView.endEditing(true)
    }
}

Notes:

  • This relies on the view hierarchy structure, which can change between iOS versions. You’ll need to test on each major iOS release.
  • Apple may reject apps that use view hierarchy traversal to modify private UI elements, though many apps have successfully used this approach.
  • The autocorrect suggestions will remain intact, as they’re part of a separate subview in the input accessory view.

Why Ulysses Does It Better

Ulysses uses a native UITextView (not WKWebView) for its editor. Native text views give full control over the input accessory view—you can easily add custom buttons while retaining autocorrect suggestions. If you’re open to switching from WKWebView to a native editor, that’s the most robust way to replicate Ulysses’ experience.


内容的提问来源于stack exchange,提问作者Dave Feldman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:16:03