Removing WKWebView Accesory bar in Swift

后端 未结 4 1760
臣服心动
臣服心动 2020-12-05 21:04

I am trying for a few days now to get this converted into Swift without really having much background with it.

This is what I\'ve got so far ... and I have been look

相关标签:
4条回答
  • 2020-12-05 21:25

    This code snippet should get you over your issue:

    class _NoInputAccessoryView: NSObject {
    
        func removeInputAccessoryViewFromWKWebView(webView: WKWebView) {
            // make sure to make UIView an optional here...
            var targetView: UIView? = nil
            for view in webView.scrollView.subviews {
                if String(view.dynamicType).hasPrefix("WKContent") {
                    targetView = view
                }
            }
    
            // only optionals can be nil
            if targetView == nil {
                return
            }
    
            let noInputAccessoryViewClassName = "\(targetView!.superclass)_NoInputAccessoryView"
            var newClass : AnyObject? = NSClassFromString(noInputAccessoryViewClassName)
            if newClass == nil {
                let uiViewClass : AnyClass = object_getClass(targetView!)
                newClass = objc_allocateClassPair(uiViewClass, noInputAccessoryViewClassName.cStringUsingEncoding(NSASCIIStringEncoding)!, 0)
            }
        } 
    

    You can also use "String(view.dynamicType)" to get the class name of the object you're looking at, as I noticed via this answer as I was researching the way to solve your problem.

    Using hasPrefix like that in both Objective-C and Swift is really hacky and perhaps a better way of hiding the keyboard could be found for production code?

    0 讨论(0)
  • 2020-12-05 21:28

    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

    0 讨论(0)
  • 2020-12-05 21:30

    Here's a slightly safer (no unsafe unwraps) version that works with Swift 4 and (at least) iOS 9 trough 12.

    fileprivate final class InputAccessoryHackHelper: NSObject {
        @objc var inputAccessoryView: AnyObject? { return nil }
    }
    
    extension WKWebView {
        func hack_removeInputAccessory() {
            guard let target = scrollView.subviews.first(where: {
                String(describing: type(of: $0)).hasPrefix("WKContent")
            }), let superclass = target.superclass else {
                return
            }
    
            let noInputAccessoryViewClassName = "\(superclass)_NoInputAccessoryView"
            var newClass: AnyClass? = NSClassFromString(noInputAccessoryViewClassName)
    
            if newClass == nil, let targetClass = object_getClass(target), let classNameCString = noInputAccessoryViewClassName.cString(using: .ascii) {
                newClass = objc_allocateClassPair(targetClass, classNameCString, 0)
    
                if let newClass = newClass {
                    objc_registerClassPair(newClass)
                }
            }
    
            guard let noInputAccessoryClass = newClass, let originalMethod = class_getInstanceMethod(InputAccessoryHackHelper.self, #selector(getter: InputAccessoryHackHelper.inputAccessoryView)) else {
                return
            }
            class_addMethod(noInputAccessoryClass.self, #selector(getter: InputAccessoryHackHelper.inputAccessoryView), method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
            object_setClass(target, noInputAccessoryClass)
        }
    }
    
    0 讨论(0)
  • 2020-12-05 21:32

    Michael Dautermann answer has got everything right, but in order to hide the accessory bar you need to swizzle the method inputAccessoryView() of UIView Class with the inputAccessoryView() of the _NoInputAccessoryView class. I have just added the couple of extra lines to the code which does this job of method swizzling.

    First you'll need a fake class to swap with

    final class FauxBarHelper: NSObject {
        var inputAccessoryView: AnyObject? { return nil }
    }
    

    Then create this method in your controller class

    /// Removes the keyboard accessory view from the web view
    /// Source: http://stackoverflow.com/a/32620344/308315 / http://stackoverflow.com/a/33939584/308315
    func _removeInputAccessoryView(webView: UIWebView) {
        var targetView: UIView? = nil
    
        for view in webView.scrollView.subviews {
            if String(describing: type(of: view)).hasPrefix("WKContent") {
                targetView = view
            }
        }
    
        guard let target = targetView else { return }
    
        let noInputAccessoryViewClassName = "\(target.superclass!)_NoInputAccessoryView"
        var newClass: AnyClass? = NSClassFromString(noInputAccessoryViewClassName)
        if newClass == nil {
            let targetClass: AnyClass = object_getClass(target)
            newClass = objc_allocateClassPair(targetClass, noInputAccessoryViewClassName.cString(using: String.Encoding.ascii)!, 0)
        }
    
        let originalMethod = class_getInstanceMethod(FauxBarHelper.self, #selector(getter: FauxBarHelper.inputAccessoryView))
        class_addMethod(newClass!.self, #selector(getter: FauxBarHelper.inputAccessoryView), method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
        object_setClass(target, newClass)
    }
    

    HTH ;)

    0 讨论(0)
提交回复
热议问题