Lab 050 – Hover Effect Namespace
Show and hide a symbol with a hover effect, using the shape of the parent view to trigger the effect.
Overview
I want a way to show a hover effect that doesn’t impact the content of the view being hovered. For example, in Looming Deadlines I’m considering a grid of countdown timers. Each one could have custom backgrounds (colors, gradients, maybe even photos). I don’t want to apply visual changes to that background. I want to show something when the view is hovered–some sort of indicator that tapping the view will do something.
What I came up with is a way to show and hide an image/symbol when the parent view is hovered. This is a bit of a hack, but it works. I have a custom view for my grid cell. In that view, I added a namespace that we can use in the hover effects.
@Namespace private var hoverNamespaceThis view is a ZStack with a shape and an image. On the image I added a hover effect using the namespace. Whenever any effects using this namespace are hovered, we’ll chang the opacity of this image.
Image(systemName: "star.fill")
.hoverEffect(in: HoverEffectGroup(hoverNamespace)) { effect, isActive, _ in
effect.opacity(isActive ? 1.0 : 0)
}On the outer ZStack, I did something similar. I didn’t want to apply a visual change to the ZStack or shape, so I needed to return an effect that does nothing. The hacky part is that we can just return the effect.
ZStack {}
.hoverEffect(in: HoverEffectGroup(hoverNamespace)) { effect, _, _ in
effect
}Here it is in practice:
Lab Code
struct Lab050: View {
var body: some View {
HStack(spacing: 12) {
ShapeHoverDemo(color: .stepRed)
ShapeHoverDemo(color: .stepGreen)
ShapeHoverDemo(color: .stepBlue)
}
.padding()
}
}
private struct ShapeHoverDemo: View {
// 1. Create a namespace
@Namespace private var hoverNamespace
@State var color: Color = .stepRed
var body: some View {
ZStack {
Rectangle()
.foregroundStyle(color)
Image(systemName: "star.fill")
.foregroundColor(.white)
.font(.largeTitle)
// 2. Use HoverEffectGroup with the namespace we defined above
.hoverEffect(in: HoverEffectGroup(hoverNamespace)) { effect, isActive, proxy in
effect.opacity(isActive ? 1.0 : 0)
}
}
// 2. Use HoverEffectGroup with the namespace we defined above
.hoverEffect(in: HoverEffectGroup(hoverNamespace)) { effect, isActive, proxy in
// Hack: just return the effect without applying it to any view
// This let's us use the shape of the ZStack to receive hover focus, without applying a visual change to it
effect
}
.aspectRatio(4/3, contentMode: .fit)
.clipShape(.rect(cornerRadius: 24))
}
}Support our work so we can continue to bring you new examples and articles.

Follow Step Into Vision