簡體   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