I’m trying to use the UITextField’s “return” key to insert a custom character. Here’s what my UITextFieldDelegate method looks like:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField insertText:@"¶"];
return NO;
}
Unfortunately, this only works some of the time:
- “one two|” –> move cursor –> “one| two” –> return –> “one¶| two” (OK)
- “onetwo|” –> return –> “onetwo¶|” (OK)
- “onetwo|” –> move cursor –> “one|two” –> return –> “onetwo¶|” (FAIL)
In the last case I would have expected “one¶|two”.
How do I ensure that the inserted text is always inserted at the cursor position?
Thanks.
The problem is when you tap the return key on the keyboard, the text field sets the selected range (the cursor position) to the end of its text before it sends you the
textFieldShouldReturn:message.You need to keep track of the cursor position so you can restore it to its prior position. Let’s say you have a reference to the text field in a property:
You’ll need an instance variable to hold the prior selected text range (from before the return key was tapped):
Then you can write a method that saves the selected text range to the instance variable:
and in
textFieldShouldReturn:, before you insert the pilcrow, you can change the selected text range back to its prior value:But how can we make the system send the
saveTextFieldSelectedTextRangemessage when we need it to?The
UITextFieldDelegateprotocol doesn’t have messages for changes to the selected range.UITextFielddoesn’t post any notifications for changes to the selected range.The
UITextInputDelegateprotocol does haveselectionWillChange:andselectionDidChange:messages, but the system sets the text field’sinputDelegateto its ownUIKeyboardImplobject when the text field begins editing, so we can’t use theinputDelegate.Key-value observing on the text field’s
selectedTextRangeproperty isn’t reliable. In my testing on the iOS 6.0 simulator, I don’t get a KVO message when I move the cursor from the middle to the end of the text by tapping the text field.The only way I can think of to reliably track changes to the text field’s selected range is by adding an observer to the run loop. On every pass through the event loop, the observer runs before event processing, so it can grab the current selected range before it changes.
So we actually need another instance variable, to hold the reference to our run loop observer:
We create the observer in
viewDidLoad:and we destroy it in both
viewDidUnloadand indealloc:To create the observer, we need a plain old C function for it to call. Here’s that function:
Now we can actually create the observer and register it with the main run loop:
and here’s how we actually deregister the observer and destroy it:
This approach works in my testing on the iOS 6.0 simulator.