быстрая позиция курсора с эмодзи

Стандартный метод, который я использую для получения позиции курсора в поле UIText, похоже, не работает с некоторыми эмодзи. Следующий код запрашивает у textField позицию курсора после вставки двух символов, сначала эмодзи, а затем буквенного символа. Когда эмодзи вставляется в текстовое поле, функция возвращает значение 2 для позиции курсора вместо ожидаемого результата 1. Любые идеи о том, что я делаю неправильно или как это исправить. Спасибо

Вот код из игровой площадки xcode:

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
}

}

код возвращает следующий вывод:

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

Я пытаюсь использовать позицию курсора, чтобы разделить текстовую строку на две части — текст, который появляется перед курсором, и текст, который появляется после курсора. Для этого я использую позицию курсора в качестве индекса для массива символов, который я создал с помощью функции карты следующим образом. Позиция курсора приводит к неправильному индексу массива с эмодзи

    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]
    }

person siege097    schedule 03.12.2018    source источник
comment
Некоторые буквы эмодзи состоят из двух символов Юникода. Некоторые состоят из трех символов Юникода. Есть много букв эмодзи, которые также состоят из четырех символов Юникода.   -  person El Tomato    schedule 03.12.2018
comment
да, но у меня возникают проблемы, когда я пытаюсь использовать положение курсора, чтобы разделить текст на две части. Я отредактирую вопрос, чтобы сделать его более понятным.   -  person siege097    schedule 03.12.2018


Ответы (1)


Ваша проблема в том, что значение NSRange, возвращаемое UITextField selectedTextRange, и offset необходимо правильно преобразовать в 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
}

Когда у вас есть String.Index, вы можете разделить строку.

if let index = getCursorPosition(textField) {
    let textBeforeCursor = textField.text![..<index]
    let textAfterCursor = textField.text![index...]
}
person rmaddy    schedule 03.12.2018
comment
Не могли бы вы объяснить причину, по которой это сработало. Я пытался отладить каждую строку, но почему-то не понял, почему positionRange играет главную роль. - person Daksh Gargas; 27.02.2019
comment
@Dennis Короче говоря, свойство selectedTextRange для UITextField основано на кодировке строки UTF-16. Это необходимо преобразовать в Swift String.Index, который основан на количестве символов. - person rmaddy; 27.02.2019
comment
Ах, логично, я думал о том же... просто хотел подтвердить. Спасибо за реверс :) - person Daksh Gargas; 27.02.2019
comment
Я использовал приведенный выше код. Ввод: abc<emoji><emoji><emoji>|cd [cursor position is before “cd”] Ожидаемый результат: Текст перед курсором: abc<emoji><emoji><emoji> Фактический результат: Текст перед курсором: abc<junk character><emoji><emoji> Есть мысли по этому поводу? - person Neelam Pursnani; 26.02.2020
comment
Я использовал ваш код для удаления символов из textBeforeCursor, но похоже, что курсор устанавливается после удаления одного символа в конец строки. У вас есть подсказка, как я могу сохранить положение курсора? - person timetraveler90; 06.05.2020