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?
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 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')"];
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With