如何改变WKWebView中的文本选择颜色?

How to change the text selection color in WKWebView?

我正在尝试更改 WKWebView 中的文本选择颜色。我尝试了几乎所有我能找到的为 UIWebView/WKWebView 提出的解决方案。

更改色调无效。为 ::selection-webkit-tap-highlight-color 标签应用 css 均无效。

它总是蓝色的。可以改吗?

这是它的示例代码。

function highlight(colour) {
var range, sel;
if (window.getSelection) {
    // IE9 and non-IE
    try {
        if (!document.execCommand("BackColor", false, colour)) {
            makeEditableAndHighlight(colour);
        }
    } catch (ex) {
        makeEditableAndHighlight(colour)
    }
} else if (document.selection && document.selection.createRange) {
    // IE <= 8 case
    range = document.selection.createRange();
    range.execCommand("BackColor", false, colour);
}}

通过Objective-C代码调用此方法

[webView stringByEvaluatingJavaScriptFromString:@"highlight('#ff0')"];

这是我在 private WebKit API hacky swizzling 上的截图,显然 不符合 AppStore 应用程序的条件。 Apple 正好为它提供了很多开源代码: https://opensource.apple.com/source/WebKit2/WebKit2-7601.1.46.9/UIProcess/ios/WKContentViewInteraction.h.auto.html https://opensource.apple.com/source/WebKit2/WebKit2-7601.1.46.9/UIProcess/ios/WKContentViewInteraction.mm.auto.html

因此 html 内容的实际选择突出显示分两个阶段进行。直到在触摸屏上按住点击 private class UIWKSelectionView 显示。亮点恰好是它的tintView属性。每个 tintView getter 调用都会生成一个新的 UIView 实例,并以高亮颜色作为背景。所以覆盖需要在每次访问后发生。

在第二阶段(用户释放点击后)所选范围由 private class UIWebTextRangeView 表示。带点的文本垂直标记为 UIWebDragDotView。突出显示发生在 updateRectViews 方法中,需要在进行颜色覆盖之前调用该方法。

最终解决方案是 validated for iOS 8 - 11,一旦颜色被覆盖,它将影响所有 WKWebView 实例。原始高亮颜色在 UIWKSelectionView & UIWebTextRangeView 中硬编码并通过 UIKit 私有方法 +[UIColor selectionHighlightColor] 获取,得到 RGBA 0 0.33 0.65 0.2.

实际代码(我选择 obj-c 是为了方便 swizzling 但这也可以在 Swift 中完成):

#import "ViewController.h"
#import <objc/runtime.h>

@import WebKit;
static IMP __original_Method_IMP_tintView;
static IMP __original_Method_IMP_updateRectViews;

//UIWebTextRangeView
void replacement_updateRectViews(UIView* self, SEL _cmd)
{
    ((void(*)(id,SEL))__original_Method_IMP_updateRectViews)(self, _cmd);
    for (UIView* view in self.subviews) {
        //isMemberOfClass would be used instead to filter out UIWebDragDotView if its color is meant to be unchanged
        if ([view isKindOfClass:NSClassFromString(@"UIWebDragDotView")]) {
            [view setValue:UIColor.redColor forKey:@"m_selectionBarColor"];
        } else {
            //These are UIView*
            view.backgroundColor = [UIColor colorWithRed:1.0 green:0 blue:0 alpha:0.2];
        }
    }
}

//UIWKSelectionView
UIView* replacement_tintView(id self, SEL _cmd)
{
    UIView* tintView = ((UIView*(*)(id,SEL))__original_Method_IMP_tintView)(self, _cmd);
    tintView.backgroundColor = [UIColor colorWithRed:1.0 green:0 blue:0 alpha:0.2]; 
    return tintView;
}

@interface ViewController ()
@end

@implementation ViewController
+ (void)load {
    __original_Method_IMP_tintView = method_setImplementation(class_getInstanceMethod(NSClassFromString(@"UIWKSelectionView"),NSSelectorFromString(@"tintView")), (IMP)replacement_tintView);
    __original_Method_IMP_updateRectViews = method_setImplementation(class_getInstanceMethod(NSClassFromString(@"UIWebTextRangeView"),NSSelectorFromString(@"updateRectViews")), (IMP)replacement_updateRectViews);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view = [WKWebView new];
    [(WKWebView*)self.view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://whosebug.com/"]]];
}
@end

从 iOS 13 开始,设置 WKWebView 的 tintColor 属性 也会更改选择颜色(和插入符号颜色)。

WKWebView *webView = ...
webView.tintColor = UIColor.redColor;

额外提示:如果您的应用程序支持深色模式,但出于某种原因 WKWebView 内容必须为浅色模式,您可以强制包含 WKWebView 的整个视图控制器具有浅色模式特征,或者您可以做:

if (@available(iOS 13.0, *)) {
    webView.tintColor = [webView.tintColor resolvedColorWithTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]];
}

这确保选择和插入符号颜色在 html

中的 "light" 内容中可见