Манипулировать содержимым вставки в WKWebView - PullRequest
0 голосов
/ 09 апреля 2020

Мне нужно манипулировать текстом, который вставляется в WKWebView (из любого источника) с асинхронной операцией, которая может занять некоторое время.

Моя оригинальная идея заключалась в использовании Javascript и конфигурации WKWebView в чтобы получить событие onpaste:

WKUserContentController *wkUController = [[WKUserContentController alloc] init];

NSString *pasteJSSource = @"document.addEventListener('onpaste', function(){ window.webkit.messageHandlers.ComposerListener.postMessage('onpaste happened!'); })";

WKUserScript *pasteScript = [[WKUserScript alloc] initWithSource:pasteJSSource injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly: NO];

[wkUController addScriptMessageHandler:self name:@"ComposerListener"];

[wkUController addUserScript:pasteScript];

webViewConfiguration.userContentController = wkUController;

Тогда мой класс реализует WKScriptMessageHandler

#pragma mark - WKScriptMessageHandler

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
    NSLog(@"message: %@", message.body);
}

(игнорировать Obj- c, swift тоже в порядке)

Но у меня есть две проблемы:

  1. userContentController:didReceiveScriptMessage: никогда не вызывается
  2. Я не знаю, как перехватить вставленный код и заменить его чем-то другим

Есть идеи о том, как решить эту проблему (даже без JS, которую я, очевидно, не знаю: P)? Спасибо.

1 Ответ

0 голосов
/ 17 апреля 2020

ДЛЯ ПОЗЫВАНИЯ:

Я уверен, что способ сделать это, используя JS, существует и он чище, но мне удалось достичь результата, используя метод swizzling:

//Method Swizzling
UIView *webContentView = self.webView.contentView;
if(webContentView != nil)
{
    //Paste:
    NSError *error;
    [webContentView swizzleMethod:@selector(paste:) withSelector:@selector(my_paste:) error:&error];
    if(error != nil)
    {
        NSLog("Failed to swizzle 'paste:' into WKContentView: %@, error);
        NSAssert(false, error);
    }

    ...
}

Где contentView:

- (UIView *)contentView
{
    return [self subviewWithClassName:@"WKContentView"];
}

Метод my_paste: должен быть частью UIResponder (который реализуется частным WKContentView)

#pragma mark - Method Swizzling UIResponder

@interface UIResponder (WebComposerSwizzling)

- (void)my_paste:(id)sender;
#define original_paste my_paste

@end

@implementation UIResponder (WebComposerSwizzling)

- (void)my_paste:(id)sender
{
    MailComposerViewController* strongComposer = sCurrentComposer;
    if (strongComposer)
        [strongComposer manipulatePasteboard:nil];
    [self original_paste:sender];
}

@end

Обратите внимание, что sCurrentComposer является переменная stati c в моем ViewController

__weak MailComposerViewController* sCurrentComposer;

Различные утилиты:

UIView+SubviewSearch

import UIKit

extension UIView {

    /// Find a subview corresponding to the className parameter, recursively.
    @objc public func subviewWithClassName(_ className: String) -> UIView? {
        if NSStringFromClass(type(of: self)) == className {
            return self
        } else {
            for subview in subviews {
                return subview.subviewWithClassName(className)
            }
        }
        return nil
    }
}

NSObject+Swizzling

import Foundation

extension NSObject {

    enum NSObjectSwizzlingError: Error {
        case originalSelectorNotFound
    }

    @objc public func swizzleMethod(_ currentSelector: Selector, withSelector newSelector: Selector) throws {
        if let currentMethod = self.instanceMethod(for: currentSelector),
            let newMethod = self.instanceMethod(for:newSelector) {
            method_exchangeImplementations(currentMethod, newMethod)
        } else {
            throw NSObjectSwizzlingError.originalSelectorNotFound
        }
    }

    @objc public func instanceMethod(for selector: Selector) -> Method? {
        let classType: AnyClass! = object_getClass(self)
        return class_getInstanceMethod(classType, selector)
    }
}

(извините за микс Swift <> OBJ- C)

...