Home > Enterprise >  SwiftUI scaled background intercepting clicks
SwiftUI scaled background intercepting clicks

Time:10-15

I'm encountering an issue with SwiftUI on macOS (12.x) where a scaled background is interfering with mouse clicks. The following is a minimal example:

struct ContentView: View {
    var body: some View {
        VStack {
            Button("Test") {
                print("Tested")
            }
            
            Image(systemName: "waveform.circle")
                .resizable()
                .frame(width: 200, height: 200)
                .background(
                    Image(systemName: "waveform.circle")
                        .resizable()
                        .foregroundColor(.red)
                        .scaleEffect(2.0)
                        .blur(radius: 20)
                        .clipped()
                )
        }
        .frame(width: 500, height: 500)
    }
}

Note that pressing the "Test" button doesn't actually work -- no message to the console is printed.

I've found a workaround using NSHostingView, but it's pretty ugly -- I'd love to know if there's a pure-SwiftUI solution to 'clip' the background View so that not only is it's appearance cut off, but also it's ability to intercept clicks.

Here's a workaround (note that turning off the scale effect also solves the issue).

struct ContentViewWorkAround: View {
    
    @State private var scaleEffectOn = true
    @State private var workAround = false
    
    @ViewBuilder private var imageBackground: some View {
        switch workAround {
        case false:
            Image(systemName: "waveform.circle")
                .resizable()
                .foregroundColor(.red)
                .scaleEffect(scaleEffectOn ? 2.0 : 1.0)
                .blur(radius: 20)
                .clipped()
                .frame(width: 200, height: 200) // ineffective at preventing clicks
        case true:
            NSHostingViewRepresented {
                Image(systemName: "waveform.circle")
                    .resizable()
                    .foregroundColor(.green)
                    .scaleEffect(2.0)
                    .blur(radius: 20)
                    .clipped()
                    .frame(width: 200, height: 200)
            }
        }
        
    }
    
    var body: some View {
        VStack {
            Button("Test") {
                print("Tested")
            }
            
            Image(systemName: "waveform.circle")
                .resizable()
                .frame(width: 200, height: 200)
                .background(
                    imageBackground
                )
            
            Toggle("Scale effect", isOn: $scaleEffectOn)
            Toggle("Workaround", isOn: $workAround)
        }
        .frame(width: 500, height: 500)
    }
}

struct NSHostingViewRepresented<V>: NSViewRepresentable where V: View {
    var content: () -> V
    
    func makeNSView(context: Context) -> NSHostingView<V> {
        NSHostingView(rootView: content())
    }
    
    func updateNSView(_ nsView: NSHostingView<V>, context: Context) { }
}

CodePudding user response:

Have you tried?

.allowsHitTesting(false)

Deactivates the clicks and allows whatever look you want.

struct ContentView: View {
    var body: some View {
        VStack {
            Button("Test") {
                print("Tested")
            }
            
            Image(systemName: "waveform.circle")
                .resizable()
                .frame(width: 200, height: 200)
                .background(
                    Image(systemName: "waveform.circle")
                        .resizable()
                        .foregroundColor(.red)
                        .scaleEffect(2.0)
                        .blur(radius: 20)
                        .clipped()
                        .allowsHitTesting(false)
                )
        }
        .frame(width: 500, height: 500)
    }
}
  • Related