文档章节

关于textfield输入框光标位置的记录

在人间2018
 在人间2018
发布于 2018/08/22 21:42
字数 1070
阅读 122
收藏 0

问题背景:

为了做特殊字符输入,做了获取和设置光标位置的功能,通过selectedTextRange属性来实现的,在iOS11上测试,一切没问题,代码如下

- (void)setCursorPosition:(int)position {
    NSRange range = NSMakeRange(position, 0);
    [self setSelectedRange:range];
}

- (int)getCursorPosition {
    NSRange range = [self selectedRange];
    NSUInteger position = range.location + range.length;
    
    return (int)position;
}

- (NSRange) selectedRange
{
    UITextPosition* beginning = self.beginningOfDocument;
    
    UITextRange* selectedRange = self.selectedTextRange;
    UITextPosition* selectionStart = selectedRange.start;
    UITextPosition* selectionEnd = selectedRange.end;
    
    NSInteger location = [self offsetFromPosition:beginning toPosition:selectionStart];
    NSInteger length = [self offsetFromPosition:selectionStart toPosition:selectionEnd];
    
    return NSMakeRange(location, length);
}

- (void) setSelectedRange:(NSRange) range
{
    UITextPosition* beginning = self.beginningOfDocument;
    
    UITextPosition* startPosition = [self positionFromPosition:beginning offset:range.location];
    UITextPosition* endPosition = [self positionFromPosition:beginning offset:range.location + range.length];
    UITextRange* selectionRange = [self textRangeFromPosition:startPosition toPosition:endPosition];
    
    [self setSelectedTextRange:selectionRange];
}

- (void)setText:(NSString *)text {
    if (![text isEqualToString:self.text]) {
        UITextRange *selection = self.selectedTextRange;
        NSInteger oldTextLength = self.text.length;
        [super setText:text];
        if (selection.empty) {
            // maintain cursor position relative to the end of the old text
            NSInteger start = [self offsetFromPosition:self.beginningOfDocument toPosition:selection.start];
            NSInteger offsetFromEnd = oldTextLength - start;
            NSInteger newOffset = text.length - offsetFromEnd;
            UITextPosition *position = [self positionFromPosition:self.beginningOfDocument offset:newOffset];
            [self setSelectedTextRange:[self textRangeFromPosition:position toPosition:position]];
        }
    }
}

后来发现在IOS9和iOS8上面获取/设置光标位置都有问题,具体表现为:

当输入框处于激活状态的时候,可以正常获取/设置,但是键盘消失的时候,不管怎么设置,selectedTextRange返回的一直是nil,导致光标位置一直为0。

这个问题描述看起来很简单清晰,但我却是经过一轮轮排查才总结出来,因为刚开始直观的怀疑是lua中sdk业务和c++包装的逻辑里面有鬼导致, 这也是个血的教训,以后再遇到类似问题,一定要先写个简单Demo进行尝试。

排查过程

刚开始怀疑是上层业务逻辑代码有鬼,所以在原生打断点观察不同系统下的执行情况,然后真的发现了区别:

iOS11:键盘消失的时候,什么也没发生;

iOS9/iOS8: 键盘消失的时候,会执行setText方法,一顿调查后,发现是iOS系统的处理,调用栈如下:

frame #0: 0x000d7ac8 UITest`-[XYTextField setText:](self=0x1659e200, _cmd="setText:", text=0x165d91b0) at XYTextField.m:60
    frame #1: 0x2685ea9a UIKit`-[UITextField _endedEditing] + 170
    frame #2: 0x2685e918 UIKit`-[UITextField willDetachFieldEditor:] + 128
    frame #3: 0x2674172c UIKit`-[UIFieldEditor becomeFieldEditorForView:] + 320
    frame #4: 0x2685e6c0 UIKit`-[UITextField _resignFirstResponder] + 228
    frame #5: 0x26835ff8 UIKit`-[UIResponder _finishResignFirstResponder] + 260
    frame #6: 0x27035532 UIKit`-[UITextField _finishResignFirstResponder] + 42
    frame #7: 0x267d731a UIKit`-[UIResponder resignFirstResponder] + 278
    frame #8: 0x2685e424 UIKit`-[UITextField resignFirstResponder] + 128

到此,直观判断,是由于低版本系统多了一步setText导致光标位置失效;再次,由于我的粗心,继续怀疑到这个setText是由业务层引起的,然后做了各种排查和无谓的斗争….

最后,才决定写个简单Demo进行验证,才最终确定setText是系统行为引起,并且在setText内部实现前后打印日志,发现执行前后的光标位置都是0,证明和setText没有关系,对我的直观判断进行了啪啪打脸~

既然和setText无关,那就只能是和输入框的状态有关了,为了证明,我监听了下面两个回调:

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
    NSLog(@"CursorPosition:%d", [_textField getCursorPosition]);
    return YES;
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    NSLog(@"CursorPosition:%d", [_textField getCursorPosition]);
}

在should的回调中输出正确,在did中输出为0,至此我才有了最终的结论:

低版本系统上,输入框非激活状态下,selectedTextRange无效。简单实验,发现确实如此。

解决方案

在苹果文档没有看到相关介绍,网上也没有找到方案,最终决定放弃实时获取光标位置,换成内存记录的方式:

在textFieldShouldEndEditing或者输入内容变化的时候(根据具体需求)获取光标位置并进行记录,供后续特殊字符的输入使用。

总结

本次解决问题虽然时间不是特别长(半天时间),但是整个过程可以说非常的业余和不专业; 虽然工作遇到的各种问题越来越多,但是基础分析解决问题的能力,还是要注意的。

自我安慰,这种情况跟现在工作涉及技术类型比较杂,业务复杂度高有关系。 以后的工作中,注意方式方法,多总结吧~

© 著作权归作者所有

在人间2018
粉丝 1
博文 23
码字总数 8813
作品 0
海淀
程序员
私信 提问
Flutter关于TextField你能知道的一切

前言 Hello,大家好这里是一个苦逼的前开发,本来干的开开心心,但是受到一些影响转行了😂,不过我就是吃不起饭,找不到工作也不会放弃开发!,真香!!我个人自学以来受到了诸多大佬文章的...

苏武难飞
07/15
0
0
TextField 输入身份证号手机号银行卡号格式化解决方案

问题描述 TextField中输入身份证号,手机号,银行卡号时每隔几位需要添加空格。当输入错误时需要从末尾或者中间删除,删除之后还要保持当前textfield的中内容保持每隔几位就有一个空格的格式...

老JI
2017/11/22
0
0
LibGDX_6.5: 常用系统控件: 文本框(TextField)

本文链接: http://blog.csdn.net/xietansheng/article/details/50187911 LibGDX 基础教程(总目录) 1. 文本框(TextField) 文本框主要用于提供给用户输入文字,获取用户输出,例如在登录时...

xietansheng
2015/12/07
0
0
iOS 指定长度字符串填充整个TextField

iOS实现指定长度字符串占满整个TextField,支持支付宝和微信密码输入样式。 实例 实现思路 采用两个TextField,一个只负责显示,重写它的drawText方法; 另一个只负责输入,设置它的字体颜色透...

LvesLi
05/10
0
0
奇技淫巧——在textarea中插入文本

今天在看自己的记录时发现在几个月前的一个项目中碰到一个这样的需求:做一些简单的计算按钮,点击这些按钮之后就在后面的输入框中显示对应的计算方法,对于一些函数类的计算方法需要将光标定...

韭菜的故事
2018/07/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

抽象同步队列AQS——AbstractQueuedSynchronizer锁详解

AQS——锁的底层支持 谈到并发,不得不谈ReentrantLock;而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)! 类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资...

须臾之余
今天
3
0
springboot配置百度UEditor 富文本详解

富文本简介 UEditor是由百度web前端研发部开发所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码... 准备工作 ueditor需要单独文...

wotrd
昨天
4
0
mysql 5.7之my.cnf配置大全

[client]port = 3306socket = /tmp/mysql.sock[mysqld]###############################基础设置######################################Mysql服务的唯一编号 每个mysql服务...

Online_Reus
昨天
3
0
MAVEN打包时引入外部链接的包

1.项目引入了ORACLE的jar包,MAVEN配置如下 2.打jar包的时候需要指定下main入口函数mainClass <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> ......

Cobbage
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部