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