SwiftUI UIView with Overlay cancels touch

Viewed 927

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!

3 Answers

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

    }

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.

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

Related