移动端如何自定义文本选择器颜色

在web端要实现修改鼠标选中文本的颜色,只需要给CSS3的::selection选择器指定颜色就可以了。但是,如果你把加了同样的HTML页面用移动端的webView来加载,然后尝试用手指触摸来选择一段文本,你会发现选择器的颜色并没有发生改变,依然是系统的蓝色。

这是为什么呢,查询资料发现移动端不支持::selection选择器。那如果我们还是想修改选择文本的颜色怎么办呢?

让我们先来看看系统是的实现原理是怎么样子的,我们用webView加载一个HTML,然后利用Deubg View Hierarchy查看当前的视图层级结构,你会发现如下的层级结构图:

系统默认效果

可以看到,当我们选择一段文本的时候,系统会创建一个跟当前视图一样大小的UITextSelectionView覆盖到当前视图上,在其上面再创建一个UITextRangeView覆盖到当前选择文本的区域上。然后再到最上面一层,有三个UIView,每个UIView对应选择的一段文字,还有两个UISelectionGrabber和UISelectionGrabberDot是显示选择区域两边的线条和圆点。

好,我们已经知道了系统的实现方式,但是UITextSelectionView、UISelectionGrabber和UISelectionGrabberDot都是系统私有的类,而且是在有选择的文本的时候才会创建出来,我们既没有直接访问它们的方法,也没法在它们被创建出来之后拿到它们来修改它们的属性。那我们怎么更改它们的颜色呢?答案是利用runtime的swizzleMethod技术,实现动态修改它们的颜色。

经过我的尝试和实践发现:对于UITextSelectionView来说,只需要交换它的setBackGroundColor:方法,在系统给它设置背景色的时候,将其背景色修改为我们想要的颜色即可。而对于UISelectionGrabber和UISelectionGrabberDot,尝试了修改其背景色和tintColor均失败,猜测它们是通过绘图绘制的颜色。这里有个偷懒的做法是在其上面贴一个跟其大小一样的自定义视图。这里需要注意的是,UITextSelectionView、UISelectionGrabber和UISelectionGrabberDot应该是全局的懒加载对象,在需要的时候创建,并不会创建多次,添加自定义的子视图的时候应该避免重复添加。所以,需要交换willMoveToSuperview:方法,在视图将要被添加到父视图上的时候,判断其类型是UISelectionGrabber或UISelectionGrabberDot时,添加我们自定义颜色的子视图即可。

这里我们把系统的蓝色修改为护眼一点的颜色,修改后的效果图如下:

自定义效果

不过,这种方式会把APP中所有文本选择器(UIWebView、UITextView等)的样式都改掉,比较暴力,如果要使用的话,建议检查下会不会影响其他地方的体验效果。通过以上思路,你是不是发现了改变其他系统类的样式的一种方式呢?

下面附上实现代码:


//

//  UIView+AOP.m

//  testPubb

//

//  Created by lumin on 2018/4/21.

//

#import "UIView+AOP.h"

#import <objc/runtime.h>

@implementation UIView (AOP)

void swizzleMethod(Class class,SEL originalSelector,SEL swizzledSelector){

    Method originalMethod = class_getInstanceMethod(class, originalSelector);

    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

    //注意class_addMethod会覆盖父类方法的实现,但是不会替换父类已经存在的方法实现。如果要改变已经存在的方法实现,使用method_setImplementation。

    //这里只是尝试覆盖父类方法的实现,如果父类没有对应方法的实现,则覆盖成功,否则覆盖失败。

    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));

    if(didAddMethod){

        //如果要替换的方法存在,它调用的是class_addMethod。如果要替换的方法不存在,它调用的是method_setImplementation。

        //这里在覆盖父类方法成功的情况下,尝试用父类原有的方法的实现替换新增方法的实现。

        class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));

    }else{

        //这里在覆盖父类方法失败的情况下,交换两个两个方法的实现。

        method_exchangeImplementations(originalMethod, swizzledMethod);

    }

}

+ (void)load

{

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        //When Swizzling a instance method,use the following:

        Class class = [self class];

        //When Swizzling a class method, use the following:

        //        Class class = object_getClass((id)self);

        swizzleMethod(class, @selector(setBackgroundColor:), @selector(aop_setBackgroundColor:));

        swizzleMethod(class, @selector(willMoveToSuperview:), @selector(aop_willMoveToSuperview:));

    });

}

- (void)aop_setBackgroundColor:(UIColor *)color

{

    if([NSStringFromClass([self.superview.superview class])isEqualToString:@"UITextRangeView"]){

        [self aop_setBackgroundColor:[UIColor colorWithRed:194/255.0 green:228/255.0 blue:193/255.0 alpha:0.5]];

    }else{

        [self aop_setBackgroundColor:color];

    }

}

- (void)aop_willMoveToSuperview:(UIView *)view

{

    NSString *className = NSStringFromClass([self class]);

    if([className isEqualToString:@"UISelectionGrabber"] || [className isEqualToString:@"UISelectionGrabberDot"]){

        UIView *coverView = [self viewWithTag:10000];

        if(!coverView){

            coverView = [[UIView alloc]initWithFrame:self.bounds];

            coverView.tag = 10000;

            [self addSubview:coverView];

        }

        if([className isEqualToString:@"UISelectionGrabberDot"]){

            coverView.layer.cornerRadius = self.bounds.size.width * 0.5;

            coverView.layer.masksToBounds = YES;

        }

        coverView.backgroundColor = [UIColor colorWithRed:194/255.0 green:228/255.0 blue:193/255.0 alpha:1.0];

    }

    [self aop_willMoveToSuperview:view];

}

@end


补充:经测试发现,WKWebView的文本选择器视图层级结构发生了很大的改变,而且实现方式也改了,所以目前这种方式在WKWebView上也不起效。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • 翻译自“Collection View Programming Guide for iOS” 0 关于iOS集合视...
    lakerszhy阅读 3,852评论 1 22
  • 翻译自“Auto Layout Guide”。 1 入门 1.1 理解自动布局 自动布局根据视图层级结构中视图上的...
    lakerszhy阅读 3,576评论 3 26
  • 太阳花,是过年的时候,老妈来给我们阳台添置的花草中的一种。最近开始陆续开花了。今天早上拍到了太阳花开的全过程,用了...
    凯德印象阅读 262评论 0 6
  • 优美的诗歌,曼妙的音乐,擦亮每一个黎明,呵护每个生命。用诗歌浸润每一个生命,为每一天注入力量的源泉。《在命运无风的...
    红巧儿阅读 252评论 0 0