Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

swift cursor position with emojis

The standard method I use to get the cursor position in a UITextfield does not seem to work with some emojis. The following code queries a textField for cursor position after insertion of two characters, first an emoji and then a letter character. When the emoji is inserted into the textField the function returns a value of 2 for the cursor position instead of the expected result of 1. Any ideas into what I am doing wrong or how to correct this. Thanks

Here is the code from an xcode playground:

class MyViewController : UIViewController {

override func loadView() {
    //setup view
    let view = UIView()
    view.backgroundColor = .white
    let textField = UITextField()
    textField.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
    textField.textColor = .black
    view.addSubview(textField)
    self.view = view

    //check cursor position
    var str = "🐊"
    textField.insertText(str)
    print("cursor position after '\(str)' insertion is \(getCursorPosition(textField))")

    textField.text = ""
    str = "A"
    textField.insertText(str)
     print("cursor position after '\(str)' insertion is \(getCursorPosition(textField))")
}

func getCursorPosition(_ textField: UITextField) -> Int {
    if let selectedRange = textField.selectedTextRange {
        let cursorPosition = textField.offset(from: textField.beginningOfDocument, to: selectedRange.end)
        return cursorPosition
    }
    return -1
}

}

the code return the following output:

cursor position after '🐊' insertion is 2
cursor position after 'A' insertion is 1

I'm trying to use the cursor position to split the text string into two pieces -- the text that occurs before the cursor and the text that occurs after the cursor. To do this I use the cursor position as an index for a character array I have created using the map function as follows. The cursor position leads to an incorrect array index with an emoji

    var textBeforeCursor = String()
    var textAfterCursor = String()
    let array = textField.text!.map { String($0) }
    let cursorPosition = getCursorPosition(textField)
    for index in 0..<cursorPosition {
        textBeforeCursor += array[index]
    }
like image 918
siege097 Avatar asked Dec 06 '25 18:12

siege097


1 Answers

Your issue is that the NSRange value returned by UITextField selectedTextRange and the offset need to be properly converted to a Swift String.Index.

func getCursorPosition(_ textField: UITextField) -> String.Index? {
    if let selectedRange = textField.selectedTextRange {
        let cursorPosition = textField.offset(from: textField.beginningOfDocument, to: selectedRange.end)
        let positionRange = NSRange(location: 0, length: cursorPosition)
        let stringOffset = Range(positionRange, in: textField.text!)!

        return stringOffset.upperBound
    }

    return nil
}

Once you have that String.Index you can split the string.

if let index = getCursorPosition(textField) {
    let textBeforeCursor = textField.text![..<index]
    let textAfterCursor = textField.text![index...]
}
like image 54
rmaddy Avatar answered Dec 09 '25 15:12

rmaddy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!