Получить диапазон от символов символов Unicode.стриж - PullRequest
0 голосов
/ 06 июня 2018

Я использую функцию textView(_: shouldChangeTextIn: replacementText:) для изменения входных данных в зависимости от ситуации.Я использую диапазон, но не могу получить Swift Range при использовании символов символов Юникода (например, (͡ ° ͜ʖ ͡ °) ).Скажите, пожалуйста, как это можно сделать?

 func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

        let maxLenthNotReached = textView.text.count + (text.count - range.length) <= maxTextLength

        if maxLenthNotReached {
            guard let newRange = Range(range, in: identityString) else { return false }
            identityString = identityString.replacingCharacters(in: newRange, with: text)
        }

        return maxLenthNotReached
    }

Пример проекта

Пример сбоя приложения http://take.ms/ojIJq

Обновление: я изменил этот метод, но у меня снова произошел сбой при удалении

"entering data" ""
"testString" "༼ つ ͡° ͜ʖ?? ͡° ༽つ( ͡° ͜ʖ? ͡"
"entering data" ""

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    debugPrint("textView.text", textView.text)
    testString = textView.text.replacingCharacters(in: Range(range, in: textView.text)!, with: text)//
    debugPrint("testString", testString)
    return true
}

Обновление 1 : я ввел эти символы в textView

( ͡° ͜ʖ ͡??°)༼ つ ͡° ͜ʖ ͡?° ༽つ

Затем я начал удалять символы справа налево после того, как три правых символа были удалены ° ༽つ, и ? car emoji ушло, тогда я не могу получить диапазон, так как я установил охрану, а приложение неЕсли я удаляю это, конечно, произойдет сбой приложения.

Полный код

class ViewController: UIViewController {

    // MARK: - IBOutlets
    @IBOutlet private weak var textView: UITextView! {
        didSet {
            textView.delegate = self
            textView.text = "( ͡° ͜ʖ ͡??°)༼ つ ͡° ͜ʖ ͡?° ༽つ"
        }
    }

    // MARK: - Properties

    private var testString = ""

}


extension ViewController: UITextViewDelegate {

    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        guard let newRange = Range(range, in: textView.text) else {
            return false
        }
        testString = textView.text.replacingCharacters(in: newRange, with: text)
        return true
    }

}

Обновление 2 : После разговора с Мартином я нашел и предоставилодна деталь, что эта проблема возникает только с клавиатурой Google, и с клавиатурой по умолчанию все работает как положено.

Исходная строка, которую я имел, была "( ͡° ͜ʖ ͡??°)༼ つ ͡° ͜ʖ ͡?° ༽つ”, эта строка используется для примера. Если я начинаю удалениеэта строка слева направо, я получаю сбой приложения,Мартин попросил показать последние данные в консоли до сбоя приложения, последняя печать перед сбоем - textView" "( ͡° ͜ʖ ͡??°)༼ つ ͡° ͜ʖ ͡?" "range" {27, 1}

1 Ответ

0 голосов
/ 08 июня 2018

Как выяснилось в обсуждении:

  • OP использует клавиатуру Google,
  • метод делегата текстового представления вызывается с

    textView.text = "( ͡° ͜ʖ ͡??°)༼ つ ͡° ͜ʖ ͡?"
    range = { 27, 1 }
    
  • , а затем

    let newRange = Range(range, in: textView.text)
    

    возвращает nil.

Причина заключается в том, что диапазон указывает на «середину»символ ?, который хранится как суррогатная пара UTF-16.Вот упрощенный автономный пример:

let text = "Hello ?!"
let range = NSRange(location: 7, length: 1)
let newRange = Range(range, in: text)
print(newRange as Any) // nil   ??

Для меня это выглядит как ошибка (в клавиатуре Google?), Но есть возможный обходной путь.

«Хитрость» заключается в определении ближайшего окружающего диапазона «составленных последовательностей символов», и вот как это можно сделать (сравните Из любого смещения UTF-16 найдите соответствующий String.Indexкоторый находится на границе символа ):

extension String {
    func safeRange(from nsRange: NSRange) -> Range<String.Index>? {
        guard nsRange.location >= 0 && nsRange.location <= utf16.count else { return nil }
        guard nsRange.length >= 0 && nsRange.location + nsRange.length <= utf16.count else { return nil }
        let from = String.Index(encodedOffset: nsRange.location)
        let to = String.Index(encodedOffset: nsRange.location + nsRange.length)
        return rangeOfComposedCharacterSequences(for: from..<to)
    }
}

Now

let newRange = textView.text.safeRange(from: range)

возвращает диапазон String, который охватывает весь символ ?.В нашем упрощенном примере:

let text = "Hello ?!"
let range = NSRange(location: 7, length: 1)
let newRange = text.safeRange(from: range)
print(newRange as Any) // Optional(Range(...))   ??
print(text.replacingCharacters(in: newRange!, with: "?")) // Hello ?!
...