21

I am trying to control the cursor position in the UITextField. Users cannot insert more than one character at a time into the middle of the text field. It moves it to the end of the textfield. So this post in SO: Control cursor position in UITextField It solves my problem. But I need to know the current cursor position.

My code looks like this:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
   if (textField.tag == 201) 
   {
     [myclass selectTextForInput:textField atRange:NSMakeRange(idx, 0)];
   }
}

It is giving me an error at idx. How do I find that?

Community
  • 1
  • 1
lakshmen
  • 28,346
  • 66
  • 178
  • 276

4 Answers4

30

UITextField conforms to the UITextInput protocol which has methods for getting the current selection. But the methods are complicated. You need something like this:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    if (textField.tag == 201) {
        UITextRange *selRange = textField.selectedTextRange;
        UITextPosition *selStartPos = selRange.start;
        NSInteger idx = [textField offsetFromPosition:textField.beginningOfDocument toPosition:selStartPos];

        [myclass selectTextForInput:textField atRange:NSMakeRange(idx, 0)];
    }
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
8

Swift version

if let selectedRange = textField.selectedTextRange {

    let cursorPosition = textField.offsetFromPosition(textField.beginningOfDocument, toPosition: selectedRange.start)
    print("\(cursorPosition)")
}

My full answer about getting and setting the cursor position is here.

Community
  • 1
  • 1
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • 1
    In Swift 4 it is textField.offset(from:to:) . It is part of the UITextInput protocol which UITextField adheres to if you are looking for it in the docs. – Dave Hubbard Dec 01 '20 at 00:28
  • 1
    @DaveHubbard, Thank you. Feel free to update my answer. I'm not doing much with Swift anymore, so I haven't been able to keep these answers up to date. – Suragch Dec 01 '20 at 05:51
4

The code you have posted won't work for determining where the cursor is. You need the get method, not the set. It should be something along the lines of:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
   if (textField.tag == 201) 
   {
         UITextRange selectedRange = [textField selectedTextRange];
         // here you will have to check whether the user has actually selected something
         if (selectedRange.empty) {
              // Cursor is at selectedRange.start
              ...
         } else {
              // You have not specified home to handle the situation where the user has selected some text, but you can use the selected range and the textField selectionAffinity to assume cursor is on the left edge of the selected range or the other
              ...
         }
   }
}

For more information - check the UITextInput protocol http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UITextInput_Protocol/Reference/Reference.html#//apple_ref/occ/intf/UITextInput

Update: @rmaddy has posted some good extra bits I missed in my response - how to handle the text position from the NSTextRange and convert NSTextPosition to int.

Kjuly
  • 34,476
  • 22
  • 104
  • 118
Dimitar K
  • 794
  • 5
  • 9
  • then how you ensure that the cursor remain in the same location after deleting a character in uitextfield.. – lakshmen May 08 '13 at 04:04
  • You cannot ensure that. To delete the character in the first place you will have to set the entire field's contents. Which will inevitably move the cursor to the end. You will have to then set it back to the position you like. – Dimitar K May 08 '13 at 04:10
1

Swift Solution.

You can set the cursor padding by subclassing the UITextfield class and implementing these two methods.

Hope it helps someone in need.

override func textRect(forBounds bounds: CGRect) -> CGRect {
    return bounds.inset(by: UIEdgeInsets.init(top: 4, left: 40, bottom: 1, right: 15))
}

override func editingRect(forBounds bounds: CGRect) -> CGRect {
    return bounds.inset(by: UIEdgeInsets.init(top: 4, left: 40, bottom: 1, right: 15))
}
GyroCocoa
  • 1,542
  • 16
  • 19