Control cursor position in UITextField
Asked Answered
D

7

52

I have a UITextField that I'm forcing formatting on by modifying the text inside the change notification handler. This works great (once I solved the reentrancy issues) but leaves me with one more nagging problem. If the user moves the cursor someplace other than the end of the string then my formatting change moves it to the end of the string. This means users cannot insert more than one character at a time into the middle of the text field. Is there a way to remember and then reset the cursor position in the UITextField?

Discrown answered 30/9, 2009 at 19:37 Comment(1)
Swift answer about getting and setting cursor position and inserting text. https://mcmap.net/q/149124/-getting-and-setting-cursor-position-of-uitextfield-and-uitextview-in-swiftSteelworker
M
68

Controlling cursor position in a UITextField is complicated because so many abstractions are involved with input boxes and calculating positions. However, it's certainly possible. You can use the member function setSelectedTextRange:

[input setSelectedTextRange:[input textRangeFromPosition:start toPosition:end]];

Here's a function which takes a range and selects the texts in that range. If you just want to place the cursor at a certain index, just use a range with length 0:

+ (void)selectTextForInput:(UITextField *)input atRange:(NSRange)range {
    UITextPosition *start = [input positionFromPosition:[input beginningOfDocument] 
                                                 offset:range.location];
    UITextPosition *end = [input positionFromPosition:start
                                               offset:range.length];
    [input setSelectedTextRange:[input textRangeFromPosition:start toPosition:end]];
}

For example, to place the cursor at idx in the UITextField input:

    [Helpers selectTextForInput:input 
                        atRange:NSMakeRange(idx, 0)];
Metaphysics answered 18/7, 2012 at 0:26 Comment(5)
The general idea is ok, but this code wants to reside in a category on NSTextField, like here: github.com/MaxZeng/UITextField-SelectedRange/blob/master/…Onwards
Sorry but it does not work, the cursor stays at the end... iOS 8.0 here.Stepsister
this works great for me on iOS 8 translated to swift, thanks!Hylo
Note that a call to becomeFirstResponder will move the cursor to the end, so if that call is needed then set the position after it.Theobald
welldone, solution is working. But one should modify a little as per your need.Detriment
K
5

Useful to position at index (Swift 3)

private func setCursorPosition(input: UITextField, position: Int) {
    let position = input.position(from: input.beginningOfDocument, offset: position)!
    input.selectedTextRange = input.textRange(from: position, to: position)
}
Knelt answered 10/11, 2016 at 12:48 Comment(0)
B
3

I've finally found a solution for this problem! You can put the text you need inserted into the system pasteboard and then paste it at the current cursor position:

[myTextField paste:self]  

I found the solution on this person's blog:
http://dev.ragfield.com/2009/09/insert-text-at-current-cursor-location.html

The paste functionality is OS V3.0 specific, but I've tested it and it works fine for me with a custom keyboard.

If you go for this solution then you should probably save the user's existing clipboard contents and restore them immediately afterwards.

Blowzed answered 24/10, 2009 at 18:32 Comment(1)
Modify the user's clipboard? That seems like something that would drive me nuts. Can you restore the clipboard when you're done with it?Assimilation
M
2

Here's the Swift version of @Chris R. – updated for Swift3

private func selectTextForInput(input: UITextField, range: NSRange) {
    let start: UITextPosition = input.position(from: input.beginningOfDocument, offset: range.location)!
    let end: UITextPosition = input.position(from: start, offset: range.length)!
    input.selectedTextRange = input.textRange(from: start, to: end)
}
Martian answered 24/10, 2016 at 13:8 Comment(0)
T
1

Feel free to use this UITextField category to get and set cursor position:

@interface UITextField (CursorPosition)

@property (nonatomic) NSInteger cursorPosition;

@end

@implementation UITextField (CursorPosition)

- (NSInteger)cursorPosition
{
    UITextRange *selectedRange = self.selectedTextRange;
    UITextPosition *textPosition = selectedRange.start;
    return [self offsetFromPosition:self.beginningOfDocument toPosition:textPosition];
}

- (void)setCursorPosition:(NSInteger)position
{
    UITextPosition *textPosition = [self positionFromPosition:self.beginningOfDocument offset:position];
    [self setSelectedTextRange:[self textRangeFromPosition:textPosition toPosition:textPosition]];
}

@end
Tasteless answered 26/10, 2016 at 21:56 Comment(0)
I
0

I don't think there is a way to place the cursor at a particular place in your UITextField (unless you got very tricky and simulated a touch event). Instead, I'd handle formatting when the user has finished editing their text (in textFieldShouldEndEditing:) and if their entry is incorrect, don't allow the text field to finish editing.

Icicle answered 30/9, 2009 at 19:43 Comment(1)
I had considered that but what I'm implementing is similar to the way you enter numbers at an ATM. The decimal is always there and as you type numbers they move to the left so entering 1-0-5-0 results first in 0.01, then 0.10, 1.05, 10.50. To do it at the end would allow 1050 in the field and then change it to 10.50 which is rather confusing to the user.Discrown
F
-2

Here is a snippet which works fine for this problem:

- (void)textFieldDidBeginEditing:(UITextField *)textField{
    UITextPosition *positionBeginning = [textField beginningOfDocument];
    UITextRange *textRange =[textField textRangeFromPosition:positionBeginning 
                                                  toPosition:positionBeginning];
    [textField setSelectedTextRange:textRange];
}

Source from @omz

Firebrick answered 11/5, 2015 at 9:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.