I use a ZStack
to show a info box above the screen content. When tapping outside the box, it should be dismissed and the tap should still be handled by the screen content.
Basically I am looking for a onTouchOutside
equivalent.
My first approach to handle the tap and to dismiss the box, was to add another full screen view below box (= between the box and the screen content) and using .onTapGesture
on this view. While this works fine to dismiss the box, the tap is consumed and thus not used by the underlying screen content.
I found several older articles about similar problems, saying that this is not possible. However, being several years old, I wonder if this is still the case. Does SwiftUI really lack such a basic and important feature?
A custom TouchPassthroughView
does not work as well. The touch is also consumed.
Are any solutions for this?
struct TouchPassthroughView: UIViewRepresentable {
var tappedOutside: () -> Void
class Coordinator: NSObject {
let tappedOutside: () -> Void
init(tappedOutside: @escaping () -> Void) {
self.tappedOutside = tappedOutside
}
@objc func handleTap(_ sender: UITapGestureRecognizer) {
tappedOutside()
}
}
func makeCoordinator() -> Coordinator {
Coordinator(tappedOutside: tappedOutside)
}
func makeUIView(context: Context) -> UIView {
let view = UIView()
view.backgroundColor = .clear
let tapGesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleTap(_:)))
tapGesture.cancelsTouchesInView = false
view.addGestureRecognizer(tapGesture)
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
struct InfoView: View {
@State private var showBox = true
var body: some View {
ZStack {
VStack {
Text("Screen Content")
.padding()
.background(Color.blue)
.onTapGesture {
print("Background tapped")
}
}
if showBox {
TouchPassthroughView {
print("Tap outside")
showBox = false
}
.ignoresSafeArea()
VStack {
Text("Box")
.padding()
.background(Color.white)
.cornerRadius(12)
}
.frame(width: 200, height: 150)
.padding(.top, 100)
.shadow(radius: 10)
}
}
}
}