以编程方式关注webview中的表单(WKWebView)

时间:2015-09-08 05:29:14

标签: ios webview

UIWebView可以实现以下功能:

[webView setKeyboardDisplayRequiresUserAction:NO]
Call some JS function

当webview为WKWebView时,你怎么能这样做?

相关:How can I get a UIWebView to focus on a form input and bring up the keyboard?

7 个答案:

答案 0 :(得分:16)

由于for (let k=0;k<teacherLength;k++) //notice < instead of <= 方法签名已更改,因此已接受的答案在iOS 11.3中不再有效。这是一个解决方法(在Obj-C中):

更新:方法签名在iOS 12.2中再次更改,以下代码已更新以反映此更改)

WebKit

答案 1 :(得分:7)

这个Swift扩展完成了这项工作,并且与11.3以及早期的版本兼容。

import Foundation
import WebKit

typealias OlderClosureType =  @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Any?) -> Void
typealias NewerClosureType =  @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void

extension WKWebView{

    var keyboardDisplayRequiresUserAction: Bool? {
        get {
            return self.keyboardDisplayRequiresUserAction
        }
        set {
            self.setKeyboardRequiresUserInteraction(newValue ?? true)
        }
    }

    func setKeyboardRequiresUserInteraction( _ value: Bool) {

        guard
            let WKContentViewClass: AnyClass = NSClassFromString("WKContentView") else {
                print("Cannot find the WKContentView class")
                return
        }

        let olderSelector: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:")
        let newerSelector: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")

        if let method = class_getInstanceMethod(WKContentViewClass, olderSelector) {

            let originalImp: IMP = method_getImplementation(method)
            let original: OlderClosureType = unsafeBitCast(originalImp, to: OlderClosureType.self)
            let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3) in
                original(me, olderSelector, arg0, !value, arg2, arg3)
            }
            let imp: IMP = imp_implementationWithBlock(block)
            method_setImplementation(method, imp)
        }

        if let method = class_getInstanceMethod(WKContentViewClass, newerSelector) {

            let originalImp: IMP = method_getImplementation(method)
            let original: NewerClosureType = unsafeBitCast(originalImp, to: NewerClosureType.self)
            let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in
                original(me, newerSelector, arg0, !value, arg2, arg3, arg4)
            }
            let imp: IMP = imp_implementationWithBlock(block)
            method_setImplementation(method, imp)
        }

    }

}

答案 2 :(得分:5)

在深入了解Webkit源代码几周之后,我已经设法通过_startAssistingNode:userIsInteracting:blurPreviousNode:userObject上的WKContentView调整并覆盖userIsInteracting值来设法在iOS 9上运行:

伪代码:

swizzle_intercept("WKContentView", "_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:", &hackAssist);

void hackAssist (id self, SEL _cmd, void* arg0, BOOL arg1, BOOL arg2, id arg3) {
    ((void (*)(id,SEL,void*,BOOL,BOOL,id))swizzle_interceptee(hackAssist))(self, _cmd, arg0, TRUE, arg2, arg3);
}

干杯!

答案 3 :(得分:5)

更新:此解决方案适用于iOS 12.2,11 *和10。*

我写了一个扩展(在Swift 4中为web.config类添加WKWebView作为计算属性,就像在UIWebView中一样。

在参考Apple的WebKit官方开源文档后,我提出了以下运行时调优:

keyboardDisplayRequiresUserAction

确保您在WKWebView上调用属性,

import Foundation
import WebKit

typealias OldClosureType =  @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Any?) -> Void
typealias NewClosureType =  @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void

extension WKWebView{
    var keyboardDisplayRequiresUserAction: Bool? {
        get {
            return self.keyboardDisplayRequiresUserAction
        }
        set {
            self.setKeyboardRequiresUserInteraction(newValue ?? true)
        }
    }

    func setKeyboardRequiresUserInteraction( _ value: Bool) {
        guard let WKContentView: AnyClass = NSClassFromString("WKContentView") else {
            print("keyboardDisplayRequiresUserAction extension: Cannot find the WKContentView class")
            return
        }
        // For iOS 10, *
        let sel_10: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:")
        // For iOS 11.3, *
        let sel_11_3: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")
        // For iOS 12.2, *
        let sel_12_2: Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")

        if let method = class_getInstanceMethod(WKContentView, sel_10) {
            let originalImp: IMP = method_getImplementation(method)
            let original: OldClosureType = unsafeBitCast(originalImp, to: OldClosureType.self)
            let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3) in
                original(me, sel_10, arg0, !value, arg2, arg3)
            }
            let imp: IMP = imp_implementationWithBlock(block)
            method_setImplementation(method, imp)
        }

        if let method = class_getInstanceMethod(WKContentView, sel_11_3) {
            let originalImp: IMP = method_getImplementation(method)
            let original: NewClosureType = unsafeBitCast(originalImp, to: NewClosureType.self)
            let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in
                original(me, sel_11_3, arg0, !value, arg2, arg3, arg4)
            }
            let imp: IMP = imp_implementationWithBlock(block)
            method_setImplementation(method, imp)
        }

        if let method = class_getInstanceMethod(WKContentView, sel_12_2) {
            let originalImp: IMP = method_getImplementation(method)
            let original: NewClosureType = unsafeBitCast(originalImp, to: NewClosureType.self)
            let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in
                original(me, sel_12_2, arg0, !value, arg2, arg3, arg4)
            }
            let imp: IMP = imp_implementationWithBlock(block)
            method_setImplementation(method, imp)
        }
    }
}

此外,请确保您的HTML TextArea element 自动对焦设置为let webView = WKWebView() webView.keyboardDisplayRequiresUserAction = false ,否则无法正常工作。

答案 4 :(得分:4)

我不得不将@Mark's answer从扩展名更改为子类,因为Swift 4.2在keyboardDisplayRequiresUserAction getter上给了我“通过此函数的所有路径都将自行调用”的警告。

import Foundation
import WebKit

typealias OldClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Any?) -> Void
typealias NewClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void

class WebView: WKWebView {

    private var _keyboardDisplayRequiresUseraction = true

    var keyboardDisplayRequiresUserAction: Bool? {
        get {
            return _keyboardDisplayRequiresUseraction
        }
        set {
            _keyboardDisplayRequiresUseraction = newValue ?? true
            setKeyboardRequiresUserInteraciton(_keyboardDisplayRequiresUseraction)
        }
    }

    private func setKeyboardRequiresUserInteraciton(_ value: Bool) {
        guard let WKContentViewClass: AnyClass = NSClassFromString("WKContentView") else {
            return print("Cannot find WKContentView class")
        }

        let oldSelector: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:")
        let newSelector: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")

        if let method = class_getInstanceMethod(WKContentViewClass, oldSelector) {
            let originalImp: IMP = method_getImplementation(method)
            let original: OldClosureType = unsafeBitCast(originalImp, to: OldClosureType.self)
            let block: @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3) in
                original(me, oldSelector, arg0, !value, arg2, arg3)
            }
            let imp: IMP = imp_implementationWithBlock(block)
            method_setImplementation(method, imp)
        }
        if let method = class_getInstanceMethod(WKContentViewClass, newSelector) {
            let originalImp: IMP = method_getImplementation(method)
            let original: NewClosureType = unsafeBitCast(originalImp, to: NewClosureType.self)
            let block: @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in
                original(me, newSelector, arg0, !value, arg2, arg3, arg4)
            }
            let imp: IMP = imp_implementationWithBlock(block)
            method_setImplementation(method, imp)
        }
    }
}

在iOS 11.2和12.0上测试

答案 5 :(得分:1)

随着方法的再次更改,更新了iOS 12.2:

Objective-C

#import <objc/runtime.h>

@implementation WebViewInjection

+ (void)allowDisplayingKeyboardWithoutUserAction {
    Class class = NSClassFromString(@"WKContentView");
    NSOperatingSystemVersion iOS_11_3_0 = (NSOperatingSystemVersion){11, 3, 0};
    NSOperatingSystemVersion iOS_12_2_0 = (NSOperatingSystemVersion){12, 2, 0};
    char * methodSignature = "_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:";

    if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_12_2_0]) {
        methodSignature = "_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:";
    }

    if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_11_3_0]) {
        SEL selector = sel_getUid(methodSignature);
        Method method = class_getInstanceMethod(class, selector);
        IMP original = method_getImplementation(method);
        IMP override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, BOOL arg3, id arg4) {
            ((void (*)(id, SEL, void*, BOOL, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3, arg4);
        });
        method_setImplementation(method, override);
    } else {
        SEL selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:");
        Method method = class_getInstanceMethod(class, selector);
        IMP original = method_getImplementation(method);
        IMP override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, id arg3) {
            ((void (*)(id, SEL, void*, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3);
        });
        method_setImplementation(method, override);
    }
}

@end

迅速:

    func setKeyboardRequiresUserInteraction( _ value: Bool) {

        guard
            let WKContentViewClass: AnyClass = NSClassFromString("WKContentView") else {
                print("Cannot find the WKContentView class")
                return
        }

        let olderSelector: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:")
        let newSelector: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")
        let newerSelector: Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")


        if let method = class_getInstanceMethod(WKContentViewClass, olderSelector) {

            let originalImp: IMP = method_getImplementation(method)
            let original: OlderClosureType = unsafeBitCast(originalImp, to: OlderClosureType.self)
            let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3) in
                original(me, olderSelector, arg0, !value, arg2, arg3)
            }
            let imp: IMP = imp_implementationWithBlock(block)
            method_setImplementation(method, imp)
        }

        if let method = class_getInstanceMethod(wkc, newSelector) {
            self.swizzleAutofocusMethod(method, newSelector, value)
        }

        if let method = class_getInstanceMethod(wkc, newerSelector) {
            self.swizzleAutofocusMethod(method, newerSelector, value)
        }
    }

    func swizzleAutofocusMethod(_ method: Method, _ selector: Selector, _ value: Bool) {
        let originalImp: IMP = method_getImplementation(method)
        let original: NewClosureType = unsafeBitCast(originalImp, to: NewClosureType.self)
        let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in
            original(me, selector, arg0, !value, arg2, arg3, arg4)
        }
        let imp: IMP = imp_implementationWithBlock(block)
        method_setImplementation(method, imp)
   }

答案 6 :(得分:0)

@jcesarmobile 的 P.S 答案,该答案适用于以下平台:iPhone SE 1 iOS 14.1、iPhone 6s 以及 iOS 14.4.2;

对于那些遭受跳跃或大小不匹配的人,当第二次加载混合的WKWebview中的内容时; 您可以使用下面的示例避免上述混乱:

setTimeout(function(){
    document.getElementById("myTextElementId").focus();
},250);

/* 在 mount 或 onload 方法中;*/

想一想:为什么第二次出现烂摊子? 可能是这样:webview第一次缓存css样式和文件,所以第二次webview加载内容更快;键盘直接跳出来,同时窗口大小合适,渲染等;所以混乱就出来了;这就是为什么我们需要延迟;