EN VI

How do I disable the focus ring (or highlight) around a button in SwiftUI on MacOS?

2024-03-16 21:00:07
How do I disable the focus ring (or highlight) around a button in SwiftUI on MacOS?

My app includes two buttons, and, wanting to be a good platform citizen, I'd like the user to be able to navigate them using the keyboard when they have System Settings > Keyboard > Keyboard navigation on.

However, this creates a really hideous highlight around my buttons that I can't seem to get rid of.

Ideally, I'd want to customize my button style when the button has focus (e.g. changing the background, or making the text bold).

See the below image, where the left button has focus and has an ugly orange ring around it, partially obscuring the drop shadow:

App screenshot, showing the highlight around the left button

Solution:

You can use a FocusState to track which button is currently selected by keyboard navigation. Using this, you can write a custom ButtonStyle that creates your custom focus effect when the button is focused. You can turn off the default focus effect (the blue outline) by using .focusEffectDisabled().

To pass the focus state to the ButtonStyle is a bit difficult. The isFocused environment value somehow doesn't work here. You can modify each button individually with a .buttonStyle modifier with different parameters like in Benzy Neez's answer, but I would prefer to use a custom environment value.

For example, here is a button style that enlarges the button when it is focused.

struct MyButtonStyle: ButtonStyle {
    @Environment(\.customFocus) var focus
    
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .scaleEffect(focus ? 1.5 : 1)
    }
}

struct CustomFocusKey: EnvironmentKey {
    static let defaultValue = false
}

extension EnvironmentValues {
    var customFocus: Bool {
        get { self[CustomFocusKey.self] }
        set { self[CustomFocusKey.self] = newValue }
    }
}

You can use a ViewModifier to pass the focus state:

struct CustomFocusModifier: ViewModifier {
    @FocusState var focus: Bool
    
    func body(content: Content) -> some View {
        content
            .focused($focus)
            .environment(\.customFocus, focus)
    }
}

extension View {
    func customFocus() -> some View {
        modifier(CustomFocusModifier())
    }
}

Example usage:

HStack {
    Button("Foo") { ... }
        .customFocus()
    Button("Bar") { ... }
        .customFocus()
}
.buttonStyle(MyButtonStyle())
.focusEffectDisabled()
Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login