繁体   English   中英

如何在当前光标位置将文本插入 UITextField?

[英]How can I insert text into UITextField at the current cursor position?

我正在尝试使用 UITextField 的“返回”键插入自定义字符。 这是我的 UITextFieldDelegate 方法的样子:

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [textField insertText:@"¶"];
    return NO;
}

不幸的是,这只在某些时候有效:

  • “一二|” -->移动光标--> "one| two" --> return --> "one¶| two" ( OK )
  • “一个人|” -->返回--> "onetwo¶|"
  • “一个人|” -->移动光标--> "one|two" -->返回--> "onetwo¶|" 失败

在最后一种情况下,我会期望“one¶|two”。

如何确保插入的文本始终插入光标位置?

谢谢。

问题是当您点击键盘上的返回键时,文本字段向您发送textFieldShouldReturn:消息之前将所选范围(光标位置)设置为其文本的结尾。

您需要跟踪光标位置,以便将其恢复到之前的位置。 假设您引用了属性中的文本字段:

@interface ViewController () <UITextFieldDelegate>

@property (strong, nonatomic) IBOutlet UITextField *textField;

@end

您需要一个实例变量来保存先前选择的文本范围(从点击返回键之前开始):

@implementation ViewController {
    UITextRange *priorSelectedTextRange_;
}

然后,您可以编写一个方法,将选定的文本范围保存到实例变量中:

- (void)saveTextFieldSelectedTextRange {
    priorSelectedTextRange_ = self.textField.selectedTextRange;
}

textFieldShouldReturn: ,在插入 pilcrow 之前,您可以将选定的文本范围更改回其先前值:

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    textField.selectedTextRange = priorSelectedTextRange_;
    [textField insertText:@"¶"];
    return NO;
}

但是我们怎样才能让系统在需要的时候发送saveTextFieldSelectedTextRange消息呢?

  • UITextFieldDelegate协议没有关于所选范围更改的消息。

  • UITextField不会针对所选范围的更改发布任何通知。

  • UITextInputDelegate协议确实有selectionWillChange:selectionDidChange:消息,但是当文本字段开始编辑时,系统将文本字段的inputDelegate设置为其自己的UIKeyboardImpl对象,因此我们不能使用inputDelegate

  • 对文本字段的selectedTextRange属性的键值观察不可靠。 在我对 iOS 6.0 模拟器的测试中,当我通过点击文本字段将光标从文本中间移动到末尾时,我没有收到 KVO 消息。

我能想到的可靠跟踪文本字段选定范围更改的唯一方法是向运行循环添加一个观察者。 每次通过事件循环时,观察者都会在事件处理之前运行,因此它可以在更改之前获取当前选定的范围。

所以我们实际上需要另一个实例变量来保存对我们的运行循环观察者的引用:

@implementation ViewController {
    UITextRange *priorSelectedTextRange_;
    CFRunLoopObserverRef runLoopObserver_;
}

我们在viewDidLoad创建观察者:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self createRunLoopObserver];
}

我们在viewDidUnloaddealloc销毁它:

- (void)viewDidUnload {
    [super viewDidUnload];
    [self destroyRunLoopObserver];
}

- (void)dealloc {
    [self destroyRunLoopObserver];
}

要创建观察者,我们需要一个普通的旧 C 函数来调用。 这是那个函数:

static void runLoopObserverCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
    __unsafe_unretained ViewController *self = (__bridge ViewController *)info;
    [self saveTextFieldSelectedTextRange];
}

现在我们可以实际创建观察者并将其注册到主运行循环中:

- (void)createRunLoopObserver {
    runLoopObserver_ = CFRunLoopObserverCreate(NULL, kCFRunLoopAfterWaiting, YES, 0, &runLoopObserverCallback, &(CFRunLoopObserverContext){
        .version = 0,
        .info = (__bridge void *)self,
        .retain = CFRetain,
        .release = CFRelease,
        .copyDescription = CFCopyDescription
    });
    CFRunLoopAddObserver(CFRunLoopGetMain(), runLoopObserver_, kCFRunLoopCommonModes);
}

下面是我们如何取消注册观察者并销毁它:

- (void)destroyRunLoopObserver {
    if (runLoopObserver_) {
        CFRunLoopRemoveObserver(CFRunLoopGetMain(), runLoopObserver_, kCFRunLoopCommonModes);
        CFRelease(runLoopObserver_);
        runLoopObserver_ = NULL;
    }
}

这种方法适用于我在 iOS 6.0 模拟器上的测试。

这里发生的情况是您没有跟踪插入点,也称为选择范围。

要做到这一点,您需要更深入地了解 UITextField 可以做什么。

使用 UITextInput(可作为 UITextField 使用的协议访问) ,您可以获取selectedTextRange ”属性,该属性告诉您插入符号(光标、插入点)的位置以及您应该插入特殊字符的位置。 如果您将对象设置为符合“ UITextInput ”协议的委托,这应该有效。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM