'SwiftUI UIView with Overlay cancels touch

I am stuck on this UI issue, working with SwiftUI and UIView.

Basically, overlay stops user interaction to follow through to the UIView.

Button(action: { print("hello") }){ Text("HERE") }
.overlay(
           LinearGradient(
               gradient: Gradient(colors: [.clear, Color.black.opacity(0.3)]),
               startPoint: .top,
               endPoint: .bottom
            ).allowsHitTesting(false))

Not working with UIView

struct ButtonView: UIViewRepresentable {

    func makeUIView(context: Context) -> UIButton {
        return UIButton(type: .close)
    }

    func updateUIView(_ uiView: UIButton, context: Context) {

    }
}

ButtonView()
.overlay(
           LinearGradient(
               gradient: Gradient(colors: [.clear, Color.black.opacity(0.3)]),
               startPoint: .top,
               endPoint: .bottom
            ).allowsHitTesting(false))

Is anyone running into same trouble? Thanks!



Solution 1:[1]

I explored a few avenues, but I could not make the overlay pass through the hit test. I think it's a bug. However I got this workaround working with:

struct ButtonView: UIViewRepresentable {

let button = UIButton(type: .close)
func makeUIView(context: Context) -> UIButton { return button }
func updateUIView(_ uiView: UIButton, context: Context) { }
func makeCoordinator() -> Coordinator { Coordinator(self) }

class Coordinator: NSObject {
    var parent: ButtonView
    var count = 0

    init(_ uiView: ButtonView) {
        self.parent = uiView
        super.init()
        self.parent.button.addTarget(self, action: #selector(clickAction(_:)), for: .touchUpInside)
    }

    @objc func clickAction(_ sender: UIButton) {
        print("---> clicked on button \(count)")
        count += 1
    }
}
}

and

let buttonView = ButtonView()
var body: some View {
    buttonView
        .overlay(LinearGradient(
            gradient: Gradient(colors: [.clear, Color.black.opacity(0.3)]),
            startPoint: .top,
            endPoint: .bottom
            ).onTapGesture { self.buttonView.button.sendActions(for: .touchUpInside)   })

    }

Solution 2:[2]

I ran into this issue too. In my case, putting a SwiftUI placeholder Text over a UITextView representable blocks touch input, even with allowsHitTesting(false).

My solution-for-dummies was to put the placeholder Text underneath the UITextView, ensuring the latter has a nil backgroundColor. Hopefully we'll see missing features like responder chain control come to SwiftUI, so we don't have to resort to using classic UIView elements.

Solution 3:[3]

Im my case work with .allowsHitTesting(false) inside .overlay method.

  var body: some View {
    PageView(pages: images)
      .frame(maxWidth: .infinity, maxHeight: 250)
      .overlay {
        TextOverlay()
          .allowsHitTesting(false)
      }
  }

Here PageView is UIPageViewController

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 workingdog support Ukraine
Solution 2 Jiropole
Solution 3 Max