Search code examples
swifthovervisionos

How to customize hover effect region in visionOS


In the Design for spatial user interfaces WWDC video around 13:00 Apple states the following in regards to hover effects:

As you're designing a lock-up, include a shape that will allow the system to display the hover effect as people focus on it. Here, we have some images with text below them. Each lock-up is a single interactive element. You need to define a custom region so it can brighten as people look at it. This helps them understand the entire area is a single element that can be selected. And remember to keep that small space in between each containing shape.

They include examples from Music app:

Music app screenshot 1 Music app screenshot 2

I'd like to do this in my app that displays Buttons in a LazyVGrid but haven't been able to achieve that result. I know you need to use the plain button style to hide the border button's background yet keep the hover effect. The hover effect appears as an oval though, instead I want it to be a rounded rectangle with additional padding around it and 4pt spacing between the hover effects, as is shown above. How can this be done? I thought .contentShape(.hoverEffect, .rect(cornerRadius: 50)) would customize the region but it doesn't seem to change anything.

Button(action: tapAction) {
    VStack {
        Color.clear
            .background(.regularMaterial)
            .aspectRatio(1, contentMode: .fit)
            .clipShape(.rect(cornerRadius: 10))
            
        HStack(spacing: 0) {
            VStack(alignment: .leading, spacing: 0) {
                Text("Line one")
                
                Text("Line two")
                    .foregroundStyle(.secondary)
            }
            
            Spacer()
            
            MarkWateredButton(action: markWateredAction)
        }
    }
    .contentShape(.hoverEffect, .rect(cornerRadius: 50).inset(by: -10)) // FIXME: Doesn't do anything
}
.buttonStyle(.plain)

Screenshot from my app


Solution

  • Yeah like YichenBman says, you do need .hoverEffect(). As a bonus, your background cornerRadius is 10. So the outer radius should be 20 with a padding of 10 to look nice and matching 😄.

    Button(action: tapAction) {
        VStack {
            Color.clear
                .background(.regularMaterial)
                .aspectRatio(1, contentMode: .fit)
                .clipShape(.rect(cornerRadius: 10))
                
            HStack(spacing: 0) {
                VStack(alignment: .leading, spacing: 0) {
                    Text("Line one")
                    
                    Text("Line two")
                        .foregroundStyle(.secondary)
                }
                
                Spacer()
                
                MarkWateredButton(action: markWateredAction)
            }
        }
        .padding(10)
        .contentShape(.hoverEffect, .rect(cornerRadius: 20)) 
        .hoverEffect()
    }
    .buttonStyle(.plain)