Search code examples
iosswiftuiuiviewrepresentable

Set exclusive touch between UIRepresentableView and SwiftUI's View


I had a UIViewRepresentable implementation for a UITextView, to be able to display and handle links on <iOS15.

...
struct TextViewWrapper: UIViewRepresentable {
    ....
     func makeUIView(context: Context) -> UITextView {
        let textView = UITextView()
        textView.text = text
        textView.textColor = foregroundColor
        textView.font = font
        textView.backgroundColor = .white
        textView.dataDetectorTypes = .link
        textView.isEditable = false
        textView.isSelectable = true
        textView.isScrollEnabled = false
        textView.delegate = context.coordinator
        textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        textView.isExclusiveTouch = true
        recalculateHeight(view: textView, result: $calculatedHeight)
        return textView
    }
   ....

}

// Coordinator
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
    onSelectLink(URL)
    return false
}
...

This is used in a SwiftUI View in the following way:

struct Content: View {
    var body: some View {
        VStack {
            TextViewWrapper(text: "Text with link https://google.com", onSelectLink: { _ in print("On select link") })
        }
        .onTapGesture { print("On tap gesture") }
    }
}

Note: This is a simplified example. But the issue is that interacting with the link is calling the callback of onSelectLink, but also is allowing the onTapGesture recognizer to be executed. I'm expecting the UITextView touch to takes precedence and the onTapGesture be ignored.


Solution

  • A possible solution is to add onTapGesture to TextViewWrapper and ignore it:

    VStack {
        TextViewWrapper(text: "Text with link https://google.com", onSelectLink: { _ in print("On select link") })
            .onTapGesture {
                // ignore this tap
            }
    }.onTapGesture {
        print("On tap gesture")
    }