Getting and Setting Cursor Position of UITextField and UITextView in Swift

IosSwiftUitextfieldUitextviewUitextposition

Ios Problem Overview


I've been experimenting with UITextField and how to work with it's cursor position. I've found a number of relation Objective-C answers, as in

But since I am working with Swift, I wanted to learn how to get the current cursor location and also set it in Swift.

The answer below is the the result of my experimentation and translation from Objective-C.

Ios Solutions


Solution 1 - Ios

The following content applies to both UITextField and UITextView.

#Useful information

The very beginning of the text field text:

let startPosition: UITextPosition = textField.beginningOfDocument

The very end of the text field text:

let endPosition: UITextPosition = textField.endOfDocument

The currently selected range:

let selectedRange: UITextRange? = textField.selectedTextRange

#Get cursor position

if let selectedRange = textField.selectedTextRange {
    
    let cursorPosition = textField.offset(from: textField.beginningOfDocument, to: selectedRange.start)
    
    print("\(cursorPosition)")
}

#Set cursor position

In order to set the position, all of these methods are actually setting a range with the same start and end values.

To the beginning

let newPosition = textField.beginningOfDocument
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)

To the end

let newPosition = textField.endOfDocument
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)

To one position to the left of the current cursor position

// only if there is a currently selected range
if let selectedRange = textField.selectedTextRange {
    
    // and only if the new position is valid
    if let newPosition = textField.position(from: selectedRange.start, offset: -1) {
        
        // set the new position
        textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
    }
}

To an arbitrary position

Start at the beginning and move 5 characters to the right.

let arbitraryValue: Int = 5
if let newPosition = textField.position(from: textField.beginningOfDocument, offset: arbitraryValue) {
    
    textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}

#Related

Select all text

textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument)

Select a range of text

// Range: 3 to 7
let startPosition = textField.position(from: textField.beginningOfDocument, offset: 3)
let endPosition = textField.position(from: textField.beginningOfDocument, offset: 7)

if startPosition != nil && endPosition != nil {
    textField.selectedTextRange = textField.textRange(from: startPosition!, to: endPosition!)
}

Insert text at the current cursor position

textField.insertText("Hello")

#Notes

  • Use textField.becomeFirstResponder() to give focus to the text field and make the keyboard appear.

  • See this answer for how to get the text at some range.

#See also

Solution 2 - Ios

In my case I had to use DispatchQueue:

func textViewDidBeginEditing(_ textView: UITextView) {
   DispatchQueue.main.async {
      textField.selectedTextRange = ...
   }
}

Nothing else from this and other threads worked.

PS: I double checked which thread did textViewDidBeginEditing was running on, and it was main thread, as all UI should run on, so not sure why that little delay using main.asynch worked.

Solution 3 - Ios

For set cursor position at your point:

textView.beginFloatingCursor(at: CGPoint(x: 10.0, y: 10.0))

 

For reset cursor position:

textView.endFloatingCursor()

Note: This example works in both Textview & Textfield.

Solution 4 - Ios

let range = field.selectedTextRange

field.text = "hello world"

field.selectedTextRange = range 

Solution 5 - Ios

I hadn't even noticed that the textView in one of my published apps actually starts from the left after you delete a word on the top line of the textView. Not only was the caret going to the left but the text would then start from the left where the caret was when you typed something! It wouldn't do it in a middle line or the bottom line, only when the line was deleted on the top.

func textViewDidChange(_ textView: UITextView)
{
   if myTextView.text!.count > 0
   {
      myTextView.textAlignment = .center
   }
}

The caret still goes to the left which is not ideal but at least with that 'if' statement added, it will only add the text from the centre as intended and won't add the text from the left.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSuragchView Question on Stackoverflow
Solution 1 - IosSuragchView Answer on Stackoverflow
Solution 2 - IosMichael RosView Answer on Stackoverflow
Solution 3 - IosParth PatelView Answer on Stackoverflow
Solution 4 - IosIvanView Answer on Stackoverflow
Solution 5 - Iosdaj mi spokójView Answer on Stackoverflow