让 UITextField 光标变成图片

作者: StanOz | 来源:发表于2016-08-11 21:53 被阅读331次

    昨晚睡觉前看到群里有人提到说要怎么把 UITextField 的光标换成图片,然后好像没人提方案,心想光标十有八九也是 UIView 的子类,就起床实现了下。主要是先看看它的本质、有什么样的行为,再根据已有的信息去实现需求。这称不上多精湛的技巧,只是看别人提这类问题问得不少,所以才写出来,万一有人看呢?

    本质

    第一步是挖掘它的本质,猜测会不会是各种私有类的实例,有没有什么方法、属性可以直接拿来用。
    直接看 View Hierarchy 是最方便快捷的了,选中那个蓝色光标可以发现它只是个 UIView 实例,那么就可以考虑修改它的 layer.contents,在上面绘制图片。

    cursor_view_hierarchy.png

    接下来是写一个方法去获取它:

    -(UIView *)findCursor:(UITextField *)tf {
        for (UIView *sv in tf.subviews) {
            if ([sv isMemberOfClass:NSClassFromString(@"UIFieldEditor")]) {
                for (UIView *v in sv.subviews) {
                    if ([v isMemberOfClass:NSClassFromString(@"_UIFieldEditorContentView")]) {
                        for (UIView *vv in v.subviews) {
                            if ([vv isMemberOfClass:NSClassFromString(@"UITextSelectionView")]) {
                                return vv.subviews.lastObject;
                            }
                        }
                    }
                }
            }
        }
        return nil;
    }
    

    行为

    除了知道它是什么以外,还需要知道它会有什么样的行为、表现:

    1. -textFieldDidBeginEditing: 中尝试获得光标,返回的结果是 nil。由此可见,delegate 回调时,输入框的光标还没被添加到 UITextSelectionView 的实例上。经测试,需要延时 0.15 秒左右才能获取得到光标。
    2. 获得光标并设置了它的 framelayer.contents 属性后,光标确实发生了变化,但随着编辑的进行,光标的 frame 又恢复为默认值。这是因为输入框成为 First Responder 或编辑时都会触发 -layoutSubviews 方法,那么 -layoutSubviews 才是比较好的地方去自定义光标的几何属性;
    3. 每次输入框成为 First Responder 时,光标都是新的 UIView 实例;
    4. 在另一个 window 跳出 UICalloutBar 实例的时候,光标的 frame 会变为默认值;
      UICalloutBar.png
    5. 长按输入框,让光标移动或进行文字选择时,frame 会变为默认值;
      Move_Cursor.png
    6. 即使在 -layoutSubviews 内改变 frame 的值,输入中文的时候 frame 仍然会不时地会变为默认值,观察不出规律;
    Input_Chinese.png
    1. 在文本中间开始编辑,光标的 size 太大会遮挡后面的文字。

    综合 1,2,3 点,写一个 UITextField 的子类在 -layoutSubviews 中获取并改变光标的内容,
    但结合 4,5,6,7 这四点,改变光标的 frame 不是个好主意,除非你就是想要那种变幻莫测的效果。

    @interface YourTextField ()
    @property (weak, nonatomic) UIView *cursor;
    @end
    
    @implementation YourTextField
    
    -(void)layoutSubviews {
        [super layoutSubviews];
        if (_cursor == nil) {
            _cursor = [self findCursor];
            _cursor.layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"img"].CGImage);
        }
    }
    
    -(UIView *)findCursor {
        for (UIView *sv in self.subviews) {
            if ([sv isMemberOfClass:NSClassFromString(@"UIFieldEditor")]) {
                for (UIView *v in sv.subviews) {
                    if ([v isMemberOfClass:NSClassFromString(@"_UIFieldEditorContentView")]) {
                        for (UIView *vv in v.subviews) {
                             if ([vv isMemberOfClass:NSClassFromString(@"UITextSelectionView")]) {
                                 return vv.subviews.lastObject;
                             }
                         }
                    }
                }
            }
        }
        return nil;
    }
    
    @end
    

    其中,cursor 是用 weak修饰的属性,之所以这么干是因为两个原因:

    1. 引用实例,避免在编辑时不断地去获取光标;
    2. 在结束编辑的时候,光标不被强引用,cursor被置为 nil 以便下次获取新实例。

    其它

    更博客不难,坚持抽时间更博客就没那么轻松了。
    下星期又是忙碌的一周。🌝

    相关文章

      网友评论

      本文标题:让 UITextField 光标变成图片

      本文链接:https://www.haomeiwen.com/subject/hsussttx.html