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

时间:2012-11-21 21:09:03

标签: objective-c ios cocoa-touch

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

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

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

  • “一二|” - > 移动光标 - > “一|二” - > return - > “one¶| two”(确定
  • “ONETWO |” - > return - > “onetwo¶|” (确定
  • “ONETWO |” - > 移动光标 - > “一|二” - > return - > “onetwo¶|” (的 FAIL

在最后一种情况下,我会期待“一¶|两”。

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

感谢。

2 个答案:

答案 0 :(得分:3)

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

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

textFieldShouldReturn:

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

@interface ViewController () <UITextFieldDelegate>

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

@end

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

@implementation ViewController {
    UITextRange *priorSelectedTextRange_;
}

并在- (void)saveTextFieldSelectedTextRange { priorSelectedTextRange_ = self.textField.selectedTextRange; } 中,在插入pilcrow之前,您可以将所选文本范围更改回其先前值:

textFieldShouldReturn:

但是,当我们需要时,我们如何让系统发送- (BOOL)textFieldShouldReturn:(UITextField *)textField { textField.selectedTextRange = priorSelectedTextRange_; [textField insertText:@"¶"]; return NO; } 消息?

  • saveTextFieldSelectedTextRange协议没有更改所选范围的消息。

  • UITextFieldDelegate不会发布任何有关所选范围更改的通知。

  • UITextField协议确实有UITextInputDelegateselectionWillChange:条消息,但系统会将文本字段的selectionDidChange:设置为自己的inputDelegate对象文本字段开始编辑,因此我们无法使用UIKeyboardImpl

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

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

所以我们实际上需要另一个实例变量来保存对run loop observer的引用:

selectedTextRange

我们在@implementation ViewController { UITextRange *priorSelectedTextRange_; CFRunLoopObserverRef runLoopObserver_; }

中创建了观察者
viewDidLoad

我们会在- (void)viewDidLoad { [super viewDidLoad]; [self createRunLoopObserver]; } viewDidUnload中将其销毁:

dealloc

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

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

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

现在我们可以实际创建观察者并使用主运行循环注册它:

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);
}

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

答案 1 :(得分:0)

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

要做到这一点,你需要更深入地了解UITextField可以做的事情。

Using UITextInput (accessible as a protocol that UITextField uses),您可以获取"selectedTextRange" property,它会告诉您插入符号(光标,插入点)的位置以及您应该插入特殊字符的位置。如果您将对象设置为符合“UITextInput”协议的委托,则 应该。