简体   繁体   中英

How to convert character index from layoutManager to String scale in swift

How to convert character index from layoutManager to String scale in swift? this is the code I'm using:

let touchPoint: CGPoint = gesture.locationOfTouch(0, inView: self.definitionLabel)
let index = layoutManager.characterIndexForPoint(touchPoint, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

please don't tell me to use advanceBy() function on the first index of the string characterset since characters like ò count two in the scale of layoutManager but swift string counts theme once.

The index returned from the NSLayoutManager is " NSString based", ie it is the number of UTF-16 code units from the start of the string to the character at the given point. (So ò actually counts as one, but Emojis 😀 count two and flags 🇧🇪 even count four.)

To convert that index to a valid Swift String index, you can use the same approach as in https://stackoverflow.com/a/30404532/1187415 :

let text = ... // the stored text
let i16 = text.utf16.startIndex.advancedBy(index, limit: text.utf16.endIndex)
// i16 is a String.UTF16View.Index
if let strIndex = String.Index(i16, within: text) {
    // strIndex is a String.CharacterView.Index
    let char = text[strIndex]
    print(char)
} 

Updated for Swift 5

Martin R's answer gave me the rough outline; here's working Swift 5 version. Mine is for a UITextView with Attributed Text but should work just as well with regular String and for UITextField and UILabel.

    func handleTap(_ sender: UIGestureRecognizer) {
        guard let textView = sender.view as? UITextView else { return }

        guard let plaintext = textView.attributedText?.string else { return }
        //guard let plaintext = textView.text else { return }

        let location = sender.location(in: textView)
        let charIndex = textView.layoutManager.characterIndex(for: location, in: textView.textContainer,
                                                          fractionOfDistanceBetweenInsertionPoints: nil)

        if let strIndex = plaintext.utf16.index(plaintext.utf16.startIndex, offsetBy: charIndex, limitedBy: plaintext.utf16.endIndex) {
            let char = plaintext[strIndex]
            print("Character tapped was \(char)")
        }
    }

    let textTap = UITapGestureRecognizer(target: self, action: #selector(handleTap))
    textView.addGestureRecognizer(textTap)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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