16
votes

I have an UITextField constructed using the storyboard. I want to not allow the user to change the position of the cursor and keep it always at the end of the text into the text field.

I tried to change the position of the cursor at the touchdown event, but when selecting the text field and then change the position of the cursor by touching the text field again, the position is changed:

- (IBAction)amountBoxTouchDown:(id)sender {
    UITextPosition *start = [amountBox positionFromPosition:[amountBox beginningOfDocument] offset:amountBox.text.length];
    UITextPosition *end = [amountBox positionFromPosition:start
                                                   offset:0];
    [amountBox setSelectedTextRange:[amountBox textRangeFromPosition:start toPosition:end]];
}

Does anyone know a solution? Thanks

7
Please explain why you want to disable cursor position. Maybe there is another solution.Marcus Adams

7 Answers

31
votes

Simply create a subclass of UITextField and override the closestPositionToPoint method:

- (UITextPosition *)closestPositionToPoint:(CGPoint)point{
    UITextPosition *beginning = self.beginningOfDocument;
    UITextPosition *end = [self positionFromPosition:beginning offset:self.text.length];
    return end;
}

Now the user will be unable to move cursor, it will be always in the end of field.

SWIFT:

override func closestPosition(to point: CGPoint) -> UITextPosition? {
    let beginning = self.beginningOfDocument
    let end = self.position(from: beginning, offset: self.text?.count ?? 0)
    return end
}
7
votes

Disable any gesture recognizers on the text field after it has become first responder. This allows it to receive the initial tap, but prevents the user from interacting with the field while it is the first responder. This keeps the system behavior of keeping the cursor at the end of the text without allowing the user to override it.

In a UITextField subclass, add the following:

SWIFT 3.1:

override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    return !isFirstResponder
}

In your storyboard, change the class of your text field to your UITextField subclass.

5
votes

If you are interested in always keeping your cursor at the end of the text, do this:

override func closestPosition(to point: CGPoint) -> UITextPosition? {
    return self.endOfDocument
}
2
votes

Think in layers and of controls as tools that you can combine to achieve functionality.

If you simply place a UIButton over top a UITextField and change the button type to Custom, you can prevent all touch events on the text field such as moving the cursor, selecting text, copying, cutting, and pasting.

By default, a custom button is transparent.

Create an action so that when the button is touched, the text field becomes the first responder.

1
votes

Ok, what you have to do is have a UITextField that is hidden.

Add that hidden text field to the view, and call becomeFirstResponder on it. From your amountBoxTouchDown: method.

In the Textfield delegate, take the text the user typed in and add it to amountBox.text. Also turn off userInteractionEnabled for the visible amountBox textField.

This creates the effect you desire.

Have a look at for some sample code Link.

1
votes

Extension of @kas-kad's solution. I created a subclass of UITextView, with this

var enableCursorMotion = true

override func closestPosition(to point: CGPoint) -> UITextPosition? {
    if enableCursorMotion {
        return super.closestPosition(to: point)
    }
    else {
        let beginning = self.beginningOfDocument
        let end = self.position(from: beginning, offset: (self.text?.count)!)
        return end
    }
}
0
votes

Why not use the textFieldDidChangeSelection method from UITextFieldDelegate?

With the below implementation the cursor is always in the end.

And you don't have to create a subclass of your UITextField

func textFieldDidChangeSelection(_ textField: UITextField) {
        let position = textField.endOfDocument
        textField.selectedTextRange = textField.textRange(from: position, to: position)
}

A drawback of this is that you can't mark the text or even see the menu with select, paste etc options.