简体   繁体   English

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

[英]Get range from unicode character symbols. Swift

I use textView(_: shouldChangeTextIn: replacementText:) function to change the input data depending on the situation. 我使用textView(_: shouldChangeTextIn: replacementText:)函数根据情况更改输入数据。 I use range, but I can not get the Swift Range when using unicode character symbols (eg ( ͡° ͜ʖ ͡°) ). 我使用范围,但使用Unicode字符符号(例如(͡°͜ʖ͡°) )时无法获得Swift Range。 Please, tell me how it can be done? 请告诉我该怎么做?

 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 示例项目

An app crash example http://take.ms/ojIJq 应用崩溃示例http://take.ms/ojIJq

Update : I changed this method but I got a crash again when deleting 更新 :我更改了此方法,但是删除时再次崩溃

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

Update 1 : I entered these characters in the textView 更新1 :我在textView中输入了这些字符

( ͡° ͜ʖ ͡🇺🇸°)༼ つ ͡° ͜ʖ ͡🏎° ༽つ

Then I started to delete the characters with the right to the left after the three right few symbols were deleted ° ༽つ , and the 🏎 car emoji has left, then I can not get the range, since I put the guard and application doesn't crash, if I remove that of course there will be app crash. 然后在删除了三个右三个符号° ༽つ after之后,我开始删除左侧右边的字符,并且🏎car emoji表情已经离开,然后我无法得到范围,因为我放了防护罩,而应用程序没有崩溃,如果我删除它,那么肯定会发生应用崩溃。

Full code 完整代码

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
    }

}

Update 2 : After talking with Martin, I found and provided one detail that this problem only happens with the Google keyboard, and with the default keyboard everything works as expected. 更新2 :与Martin交谈后,我发现并提供了一个细节,该问题仅在Google键盘上发生,并且在默认键盘下,一切正常。

The original line I had was "( ͡° ͜ʖ ͡🇺🇸°)༼ つ ͡° ͜ʖ ͡🏎° ༽つ” , this line is used for an example.If I start deleting this line from left to right, I get the app crash, Martin asked to show the latest data in the console before the app crashes, last print before crash is textView" "( ͡° ͜ʖ ͡🇺🇸°)༼ つ ͡° ͜ʖ ͡🏎" "range" {27, 1} 我原来的行是"( ͡° ͜ʖ ͡🇺🇸°)༼ つ ͡° ͜ʖ ͡🏎° ༽つ” ,以该行为例。如果我开始从左到右删除该行,应用程序崩溃时,马丁要求在应用程序崩溃前在控制台中显示最新数据,崩溃前的最后打印为textView" "( ͡° ͜ʖ ͡🇺🇸°)༼ つ ͡° ͜ʖ ͡🏎" "range" {27, 1}

As it turned out in the discussion: 在讨论中发现:

  • OP is using the Google keyboard, OP正在使用Google键盘,
  • the text view delegate method is called with 使用以下命令调用文本视图委托方法

     textView.text = "( ͡° ͜ʖ ͡🇺🇸°)༼ つ ͡° ͜ʖ ͡🏎" range = { 27, 1 } 
  • and then 接着

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

    returns nil . 返回nil

The reason is that the range points into the “middle” of the 🏎 character, which is stored as a UTF-16 surrogate pair. 原因是范围指向🏎字符的“中间”,该字符存储为UTF-16代理对。 Here is a simplified self-contained example: 这是一个简化的独立示例:

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

This looks like a bug (in the Google keyboard?) to me, but there is a possible workaround. 对我来说,这看起来像是个bug(在Google键盘上?),但是有可能的解决方法。

The “trick” is to determine the closest surrounding range of “composed character sequences,” and here is how that can be done (compare From any UTF-16 offset, find the corresponding String.Index that lies on a Character boundary ): “技巧”是确定“组合字符序列”的最接近范围,这是可以做到的(比较任何UTF-16偏移量,找到位于Character边界上的相应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)

returns a String range that enclosed the entire 🏎 character. 返回包含整个🏎字符的String范围。 In our simplified example: 在我们的简化示例中:

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 🚂!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM