How to block touches specifically by inserting an invisible and intercepting view in SwiftUI?

103 views Asked by At

In SwiftUI I would like to conditionally block touches by inserting a transparent view in the hierarchy and intercepting them.

I don't want to do it in a more abstract way because I find this simpler.

Here is the starting point:

struct ContentView: View {
    
    @State var blockTouches:Bool = false
    
    var body: some View {
        
        VStack{
            Text("toggle touch blocking")
                .padding()
                .padding()
                .onTapGesture {
                    blockTouches.toggle()
                }
            ZStack{
                VStack{
                    Color.red.opacity(0.5).onTapGesture {
                        print("TOUCHED RED")
                    }
                    Color.blue.opacity(0.5).onTapGesture {
                        print("TOUCHED BLUE")
                    }
                }
                if blockTouches {
                    // The view is not rendered and touches are not blocked here.
                    Color.clear.onTapGesture {
                        print("TOUCH BLOCKED!")
                    }
                }
            }
        }
    }
}

However, since we have used Color.clear, the framework decides it doesn't need to render that view at all, and thus the touches don't get blocked.

I tried to trick it in various ways like this:

Rectangle()
    .fill(Color.clear)
    .onTapGesture {
        print("TOUCH BLOCKED!")
        }
    .background(Color.clear)

But only found this to work (the slight opacity, that is):

Color.white.opacity(0.0001).onTapGesture {
    print("TOUCH BLOCKED!")
}

Isn't there a better/normal way to do it?

1

There are 1 answers

0
workingdog support Ukraine On

You could try this simple approach, using allowsHitTesting(...)

struct ContentView: View {
    @State var blockTouches = false
    
    var body: some View {
        VStack {
            Button("Touch blocking \(blockTouches ? "ON" : "OFF")") {
                blockTouches.toggle()
            }.buttonStyle(.bordered)
            
            ZStack {
                VStack {
                    Color.red.opacity(0.5)
                        .onTapGesture {
                            print("TOUCHED RED")
                        }
                    Color.blue.opacity(0.5)
                        .onTapGesture {
                            print("TOUCHED BLUE")
                        }
                }.allowsHitTesting(!blockTouches) // <--- here
            }
        }
    }
}

You can of course add this to any of the Color individualy.