简体   繁体   中英

How to change the text selection color in WKWebView?

I'm trying to change text selection color in WKWebView . I tried pretty much all the solutions proposed for UIWebView / WKWebView that I was able to find.

Changing tint color does not work. Applying css for ::selection , -webkit-tap-highlight-color tags works neither.

在此处输入图片说明

It's always blue. Is it possible to change it?

Here's the sample code for it.

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);
}}

call this method by Objective-C code

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

As of iOS 13, setting WKWebView's tintColor property also changes the selection color (and the caret color).

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

Extra tip: if you have an app that has dark mode support but for some reason the WKWebView content must be light mode, you can either force the whole view controller containing the WKWebView to have the light mode trait or you can do:

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

this ensures that the selection and caret colors will be visible with "light" content in the html

在此处输入图片说明

Here's my shot at private WebKit API hacky swizzling obviously not eligible for an AppStore app. Apple happens to provide lots of open source code for it: 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

So the actual selection highlight for html content happens in 2 phases. Until the tap is being held on the touch screen private class UIWKSelectionView is shown. The highlight happens to be its tintView property. Each tintView getter call generates a new UIView instance with highlight color as the background. So the override needs to happen after each access.

In the second phase (after user releases the tap) the selected range is represented by private class UIWebTextRangeView . The text vertical markers with dots are UIWebDragDotView . The highlighting happens in updateRectViews method , which needs to be called before doing the color overriding.

The final solution is validated for iOS 8 - 11 , once the color is overriden it will affect all WKWebView instances. The original highlight color is hardcoded in UIWKSelectionView & UIWebTextRangeView and fetched through UIKit private method +[UIColor selectionHighlightColor] which yields RGBA 0 0.33 0.65 0.2 .

The actual code (I chose obj-c for swizzling convenience but this could also be done in 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://stackoverflow.com/"]]];
}
@end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM