16

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

Kiran Sarvaiya
  • 1,318
  • 1
  • 13
  • 37
MahdiS
  • 211
  • 1
  • 2
  • 7

7 Answers7

32

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
}
kas-kad
  • 3,736
  • 1
  • 26
  • 45
  • 1
    Nice solution. override func closestPosition(to point: CGPoint) -> UITextPosition? { let begining = self.beginningOfDocument let end = self.position(from: begining, offset: (self.text?.characters.count)!) return end } – Arun_ Oct 18 '16 at 18:18
  • Works great. Thank you – aaronium112 Jan 16 '18 at 18:35
  • It worked, now how to move the cursor to selected position using the same override method. – Harsha Oct 16 '18 at 05:54
  • @Harsha find appropriate question in Stackoverflow or create a new one if none found, – kas-kad Oct 16 '18 at 09:21
  • 1
    This should be the accepted answer. Nice and clean solution – Miki Dec 25 '20 at 16:02
7

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.

Sean Kladek
  • 4,396
  • 1
  • 23
  • 29
5

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
}
kotyara
  • 71
  • 1
  • 5
  • This should have been the accepted answer, cause why take the `beginningOfDocument` and add offset (like in the most upvoted answer) if you could simply set `endOfDocument`? :) Not to mention that disabling interaction completely will prevent you from other actions (like selecting/pasting etc). Kudos to you, @kotyara! – roxanneM Jul 21 '20 at 14:53
  • This also breaks text selection and the cursor can still be moved with the cursor keys of a hardware keyboard. – Sebastian Kirsche Nov 17 '20 at 20:18
2

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.

Marcus Adams
  • 53,009
  • 9
  • 91
  • 143
  • I tried with UIButton and UIView on top of UITextField. i disable both userInteactionEnable = NO and made it clear color, but it's not working. UITextField by view hierarchy will be next who gets touch events, so it will allow you to position view. I use @purrrminator solution. – toxicsun Aug 03 '16 at 12:08
  • 4
    I don't suggest you add more UI when you can just modify the behaviour of the textfield you're using. – Gil Sand Feb 27 '17 at 22:44
  • 1
    But how do you block the two finger ipad keyboard swipe, this will also move the cursor without touching the uitextfield – Steven B. Oct 20 '17 at 12:22
  • And also 3D Touch on the iPhone keyboard has the same effect as two fingers on the iPad keyboard – Kane Cheshire Jun 13 '18 at 11:31
1

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.

likeitlikeit
  • 5,563
  • 5
  • 42
  • 56
Bonnie
  • 4,943
  • 30
  • 34
  • I have tried this, but the keyboard will dismiss after disabling the user interaction!! – MahdiS May 07 '13 at 12:48
  • I have written the code under the view controller file. I have set textField.userInteractionEnabled = NO just in the touchDown function, I can't have the keyboard displayed. The code is mentioned in my question above, I have just deleted the call of the textField.userInteractionEnabled = NO. – MahdiS May 07 '13 at 13:02
  • This is a good solution but it is not adapted to my custom use. Thanks any way. – MahdiS May 07 '13 at 14:39
  • 2
    I don't like this because it looks like a hack and is unclear to the reader – Gil Sand Feb 27 '17 at 22:43
1

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
    }
}
Chris Prince
  • 7,288
  • 2
  • 48
  • 66
0

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.

yannisalexiou
  • 605
  • 12
  • 25