How to detect touch on NSTextAttachment

前端 未结 7 904
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-14 03:24

What is the best way to detect when user taps on NSTextAttachment on iOS?

I think that one of the ways would be checking for the character on carret\'s

7条回答
  •  执念已碎
    2020-12-14 03:34

    I have altered Drew's gesture recognizer here to subclass UITapGestureRecognizer rather than UIGestureRecognizer.

    This offers one advantage in that it only detects discrete taps as opposed to the beginning of a scroll.

    import UIKit
    import UIKit.UIGestureRecognizerSubclass
    
    // Modified from: https://stackoverflow.com/a/49153247/658604
    
    /// Recognizes a tap on an attachment, on a UITextView.
    /// The UITextView normally only informs its delegate of a tap on an attachment if the text view is not editable, or a long tap is used.
    /// If you want an editable text view, where you can short cap an attachment, you have a problem.
    /// This gesture recognizer can be added to the text view, and will add requirments in order to recognize before any built-in recognizers.
    class AttachmentTapGestureRecognizer: UITapGestureRecognizer {
    
        typealias TappedAttachment = (attachment: NSTextAttachment, characterIndex: Int)
    
        private(set) var tappedState: TappedAttachment?
    
        override func touchesBegan(_ touches: Set, with event: UIEvent) {
            tappedState = nil
    
            guard let textView = view as? UITextView else {
                state = .failed
                return
            }
    
            if let touch = touches.first {
                tappedState = evaluateTouch(touch, on: textView)
            }
    
            if tappedState != nil {
                // UITapGestureRecognizer can accurately differentiate discrete taps from scrolling
                // Therefore, let the super view evaluate the correct state.
                super.touchesBegan(touches, with: event)
    
            } else {
                // User didn't initiate a touch (tap or otherwise) on an attachment.
                // Force the gesture to fail.
                state = .failed
            }
        }
    
        /// Tests to see if the user has tapped on a text attachment in the target text view.
        private func evaluateTouch(_ touch: UITouch, on textView: UITextView) -> TappedAttachment? {
            let point = touch.location(in: textView)
            let glyphIndex: Int? = textView.layoutManager.glyphIndex(for: point, in: textView.textContainer, fractionOfDistanceThroughGlyph: nil)
            let index: Int? = textView.layoutManager.characterIndexForGlyph(at: glyphIndex ?? 0)
            guard let characterIndex = index, characterIndex < textView.textStorage.length else {
                return nil
            }
            guard NSTextAttachment.character == (textView.textStorage.string as NSString).character(at: characterIndex) else {
                return nil
            }
            guard let attachment = textView.textStorage.attribute(.attachment, at: characterIndex, effectiveRange: nil) as? NSTextAttachment else {
                return nil
            }
            return (attachment, characterIndex)
        }
    }
    

提交回复
热议问题