从unicode字符符号获取范围。迅速

时间:2018-06-06 13:13:45

标签: ios swift range nsrange

我使用textView(_: shouldChangeTextIn: replacementText:)函数根据情况更改输入数据。我使用范围,但在使用unicode字符符号时无法获得Swift范围(例如(͡°͜ʖ͜ʖ°))。请告诉我怎么做?

 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
    }

Example project

应用程序崩溃示例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

中输入了这些字符
( ͡° ͜ʖ ͡°)༼ つ ͡° ͜ʖ ͡° ༽つ

然后我在删除了三个正确的几个符号° ༽つ之后开始删除左边的字符,并且汽车表情符号已经离开,然后我无法获得范围,因为我放了守卫并且应用程序不会崩溃,如果我删除它当然会有应用程序崩溃。

完整代码

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 :在与Martin交谈后,我发现并提供了一个详细信息,只有Google键盘会出现此问题,并且使用默认键盘,一切都按预期工作。

我原来的行是"( ͡° ͜ʖ ͡°)༼ つ ͡° ͜ʖ ͡° ༽つ”,这一行用作示例。如果我从左到右开始删除这行,我得到应用程序崩溃,Martin要求在控制台中显示最新数据在应用程序崩溃之前,崩溃前的最后一次打印是textView" "( ͡° ͜ʖ ͡°)༼ つ ͡° ͜ʖ ͡" "range" {27, 1}

1 个答案:

答案 0 :(得分:2)

正如讨论结果所示:

  • 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   

对我来说,这看起来像是一个错误(在谷歌键盘中?),但有一种可能的解决方法。

“技巧”是确定最近的“组成”范围 字符序列,“这是如何做到的 (比较From any UTF-16 offset, find the corresponding String.Index that lies on a Character boundary):

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

现在

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 !