如何从 objective-C 的 WKWebView 中获取选定的文本

How do I get the selected text from a WKWebView from objective-C

我有一个 WKWebView。

当用户右键单击它时,我可以在 objective-c 方法中自定义上下文菜单。只有当用户在 WKWebView 中选择了一些文本时,我才想添加一个菜单项。当然,稍后我需要检索选定的文本以对其进行处理。

如何从 objective-c 的 WKWebView 中检索选择,确保它只是文本并获取该文本?

谢谢

以下是我如何做到这一点的。不是一个完美的解决方案,但已经足够好了。

一般说明

似乎WKWebView内部发生的任何事情都必须在Javascript中进行管理。 Apple 提供了一个在 Javascript 世界和 Objective-C(或 Swift)世界之间交换信息的框架。该框架基于一些消息,这些消息从 Javascript 世界发送,并通过可以安装在 WKWebView 中的消息处理程序在 Objective-C(或 Swift)世界中捕获。

第一步 - 安装消息处理程序

在objective-C(或Swift)世界中定义一个对象,负责接收来自Javascript世界的消息。我为此使用了我的视图控制器。下面的代码将视图控制器安装为 "user content controller",它将接收名为 "newSelectionDetected" 的事件,这些事件可以从 Javascipts

发送
- (void)viewDidLoad
{
    [super viewDidLoad];

    //  Add self as scriptMessageHandler of the webView
    WKUserContentController *controller = self.webView.configuration.userContentController ;
    [controller addScriptMessageHandler:self
                                   name:@"newSelectionDetected"] ;
    ... the rest will come further down...

第二步 - 在视图

中安装一个Javascript

此 Javascript 将检测选择更改,并通过名为 "newSelectionDetected"

的消息发送新选择
- (void)    viewDidLoad
{
    ...See first part up there...

    NSURL       *scriptURL      = .. URL to file DetectSelection.js...
    NSString    *scriptString   = [NSString stringWithContentsOfURL:scriptURL
                                                           encoding:NSUTF8StringEncoding
                                                              error:NULL] ;

    WKUserScript    *script = [[WKUserScript alloc] initWithSource:scriptString
                                                     injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
                                                  forMainFrameOnly:YES] ;
    [controller addUserScript:script] ;
}

和 Javascript :

function getSelectionAndSendMessage()
{
    var txt = document.getSelection().toString() ;
    window.webkit.messageHandlers.newSelectionDetected.postMessage(txt) ;
}
document.onmouseup = getSelectionAndSendMessage ;
document.onkeyup   = getSelectionAndSendMessage ;
document.oncontextmenu  = getSelectionAndSendMessage ;

第三步-接收并处理事件

现在,每当我们在 WKWebView 中按下鼠标或按下键时,选择(可能为空)将被捕获并通过消息发送到 Objective-C 世界。

我们只需要视图控制器中的处理程序来处理该消息

- (void)    userContentController:(WKUserContentController*)userContentController
          didReceiveScriptMessage:(WKScriptMessage*)message
{
    // A new selected text has been received
    if ([message.body isKindOfClass:[NSString class]])
    {
        ...Do whatever you want with message.body which is an NSString...
    }
}

我做了一个class继承自WKWebView,有一个NSString属性'selectedText'。所以我在这个处理程序中所做的是将接收到的 NSString 存储在这个 属性.

第四步 - 更新上下文菜单

在我的 WKWebView 的女儿 class 中,我只是重写了 willOpenMenu:WithEvent: 如果 selectedText 不为空则添加菜单项的方法。

- (void)    willOpenMenu:(NSMenu*)menu withEvent:(NSEvent*)event
{
    if ([self.selectedText length]>0)
    {
        NSMenuItem  *item   = [[NSMenuItem alloc] initWithTitle:@"That works !!!"
                                                         action:@selector(myAction:)
                                                  keyEquivalent:@""] ;
        item.target = self ;
        [menu addItem:item] ;
    }
}

- (IBAction)    myAction:(id)sender
{
    NSLog(@"tadaaaa !!!") ;
}

现在为什么不理想?好吧,如果您的网页已经设置了 onmouseup 或 onkeyup,我会覆盖它。

但正如我所说,对我来说已经足够好了。

编辑:我在 javascript 中添加了 document.oncontextmenu 行,解决了我有时遇到的奇怪的选择行为。

Swift 5个翻译

webView.configuration.userContentController.add(self, name: "newSelectionDetected")
let scriptString = """
    function getSelectionAndSendMessage()
    {
        var txt = document.getSelection().toString() ;
        window.webkit.messageHandlers.newSelectionDetected.postMessage(txt);
    }
    document.onmouseup = getSelectionAndSendMessage;
    document.onkeyup = getSelectionAndSendMessage;
    document.oncontextmenu = getSelectionAndSendMessage;
"""
let script = WKUserScript(source: scriptString, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
webView.configuration.userContentController.addUserScript(script)
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    // Use message.body here
}

只需要评估简单的js脚本

NSString *script = @"window.getSelection().toString()";

使用evaluateJavaScript方法

[wkWebView evaluateJavaScript:script completionHandler:^(NSString *selectedString, NSError *error) {
    
}];

Swift版本

let script = "window.getSelection().toString()"
wkWebView.evaluateJavaScript(script) { selectedString, error in
        
}