UITextField的光标操作扩展(UITextRange,UITextPosition)(处理限定字数输入)

本文介绍了如何在iOS开发中实现自定义键盘的光标精确控制,包括获取光标位置、移动光标的方法。通过扩展UITextField,实现了获取当前光标偏移、设定光标偏移及从文首偏移的功能。针对中文输入法的特殊情况,提供了避免字符统计错误的解决方案,确保在输入限制条件下仍能正常工作。
摘要由CSDN通过智能技术生成

简介

iOS开发中,有时候需要完全自主的定义键盘,用于完整的单词输入,例如计算机应用中,需要一次性的输入sin(,在移动光标时要完整的跳过sin(,在删除时也要完整的删除,这就需要对光标的位置进行精确控制,而iOS并没有相关的函数可以直接操作光标,只给出了选择某个区域的功能,本文将介绍基于区域选择设计的UITextField扩展,用于获取光标位置以及移动光标。

实现原理

  • 光标位置的获取 
    在textField中,有一个属性称之为selectedTextRange,这个属性为UITextRange类型,包含[start,end)两个值,通过实验我们可以发现,在没有文字被选取时,start代表当前光标的位置,而end=0;当有区域被选择时,start和end分别是选择的头和尾的光标位置,从0开始,并且不包含end,例如选择了0~3的位置,则start=0,end=4。

  • 光标的移动 
    通过setSelectedTextRange:方法可以设置选取范围,我们只要设置一个选取单个字符的范围,即可移动光标而不选中。

关键属性

  1. // 内容为[start,end),无论是否有选取区域,start都描述了光标的位置。

  2. @property (nullable, readwrite, copy) UITextRange *selectedTextRange;

  3. // 文首和文尾的位置

  4. @property (nonatomic, readonly) UITextPosition *beginningOfDocument;

  5. @property (nonatomic, readonly) UITextPosition *endOfDocument;

  • 关键方法
  1. // 获取以from为基准的to的偏移,例如abcde,光标在c后,则光标相对文尾的偏移为-2。

  2. - (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)toPosition;

  3. // 获取以from为基准偏移offset的光标位置。

  4. - (nullable UITextPosition *)positionFromPosition:(UITextPosition *)position offset:(NSInteger)offset;

  5. // 创建一个UITextRange

  6. - (nullable UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition;

具体实现

  • 下面的代码为UITextField创建了一个分类(Category),包含三个方法,分别用于获取光标位置、从当前位置偏移、从头偏移。
  1. #import <UIKit/UIKit.h>

  2.  
  3. @interface UITextField (Extension)

  4.  
  5. - (NSInteger)curOffset;

  6. - (void)makeOffset:(NSInteger)offset;

  7. - (void)makeOffsetFromBeginning:(NSInteger)offset;

  8.  
  9. @end

  10. #import "UITextField+Extension.h"

  11.  
  12. @implementation UITextField (Extension)

  13.  
  14. - (NSInteger)curOffset{

  15.  
  16. // 基于文首计算出到光标的偏移数值。

  17. return [self offsetFromPosition:self.beginningOfDocument toPosition:self.selectedTextRange.start];

  18.  
  19. }

  20.  
  21. - (void)makeOffset:(NSInteger)offset{

  22.  
  23. // 实现原理是先获取一个基于文尾的偏移,然后加上要施加的偏移,再重新根据文尾计算位置,最后利用选取来实现光标定位。

  24. UITextRange *selectedRange = [self selectedTextRange];

  25. NSInteger currentOffset = [self offsetFromPosition:self.endOfDocument toPosition:selectedRange.end];

  26. currentOffset += offset;

  27. UITextPosition *newPos = [self positionFromPosition:self.endOfDocument offset:currentOffset];

  28. self.selectedTextRange = [self textRangeFromPosition:newPos toPosition:newPos];

  29.  
  30. }

  31.  
  32. - (void)makeOffsetFromBeginning:(NSInteger)offset{

  33.  
  34. // 先把光标移动到文首,然后再调用上面实现的偏移函数。

  35. UITextPosition *begin = self.beginningOfDocument;

  36. UITextPosition *start = [self positionFromPosition:begin offset:0];

  37. UITextRange *range = [self textRangeFromPosition:start toPosition:start];

  38. [self setSelectedTextRange:range];

  39. [self makeOffset:offset];

  40.  
  41. }

  42.  
  43. @end

 

2.应用:http://www.cnblogs.com/blogfantasy/p/5304260.html

 

 

首先监听UITextfield值的改变有以下三种方法:

1、KVO方式
[textField addObserver:self forKeyPath:@"text" options:0 context:nil];
2、直接添加监视
[textField addTarget:self  action:@selector(valueChanged:)  forControlEvents:UIControlEventAllEditingEvents];
3、注册消息通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldChanged:) name:UITextFieldTextDidChangeNotification object:textField];

此方法有局限,1能监听输入框的直接赋值,2  3能监听输入框的输入.

此外就是可以使用ReactiveCocoa,来同时监听输入和赋值

此外就是,使用textview的代理方法,

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

这样实现的结果是:对于纯字符的统计没有什么问题,当输入的字符超过限制时输入框便截取最大限制长度的字符串。但是,有个问题,当使用拼音输入法时,该委托方法中的最后一个参数string接受的是输入的字母,而不是选择的汉字,造成的结果是,当想输入文字“我在编程”,输入拼音“wozaibiancheng”,每输入一个字母便会进入委托方法,统计的字符长度是字母的长度,实际上汉字还未超过限制长度,但是字母的长度超过了导致无法继续输入。

而且,致命的是,这个委托方法并不响应,选中候选汉字的过程,这就没有办法重新修正字符长度的统计了。

所以实现的代码如下。

<1>在init时候注册notification:

[[NSNotificationCenter defaultCenter]addObserver:selfselector:@selector(textFiledEditChanged:) 
                                           name:@"UITextFieldTextDidChangeNotification" 
                                         object:myTextField]; 

<2>实现监听方法:

 -(void)textFiledEditChanged:(NSNotification *)obj{

   UITextField *textField = (UITextField *)obj.object; 
    
   NSString *toBeString = textField.text; 
   NSString *lang = [[UITextInputMode currentInputMode] primaryLanguage]; // 键盘输入模式 
   if ([lang isEqualToString:@"zh-Hans"]) { // 简体中文输入,包括简体拼音,健体五笔,简体手写 
       UITextRange *selectedRange = [textField markedTextRange]; 
       //获取高亮部分 
       UITextPosition *position = [textFieldpositionFromPosition:selectedRange.start offset:0]; 
       // 没有高亮选择的字,则对已输入的文字进行字数统计和限制 
       if (!position) { 
           if (toBeString.length > kMaxLength) { 
               textField.text = [toBeString substringToIndex:kMaxLength]; 
           } 
       } 
       // 有高亮选择的字符串,则暂不对文字进行统计和限制 
       else{ 
          
       } 
   } 
   // 中文输入法以外的直接对其统计限制即可,不考虑其他语种情况 
   else{ 
       if (toBeString.length > kMaxLength) { 
           textField.text = [toBeString substringToIndex:kMaxLength]; 
       } 
   } 
}

<3>在dealloc里注销掉监听方法,切记!

  -(void)dealloc{

   [[NSNotificationCenter defaultCenter]removeObserver:self
                                           name:@"UITextFieldTextDidChangeNotification"
                                         object:_albumNameTextField];
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值