Мое приложение включает в себя две кнопки, и, желая быть хорошим пользователем платформы, я бы хотел, чтобы пользователь мог перемещаться по ним с помощью клавиатуры, когда у них включен System Settings > Keyboard > Keyboard navigation.
Однако это создает действительно ужасную подсветку вокруг моих кнопок, от которой я не могу избавиться.
В идеале я бы хотел настроить стиль своей кнопки, когда кнопка находится в фокусе (например, изменить фон или сделать текст жирным).
См. изображение ниже, где левая кнопка находится в фокусе и окружена уродливым оранжевым кольцом, частично скрывающим тень:





Цвет выделения фокуса зависит от цвета акцента. Поэтому, если вы хотите изменить его на что-то более нейтральное, вы можете изменить цвет акцента.
Для большего контроля над укладкой вы можете применить свои ButtonStyle:
FocusState, а затем передать флаг стилю..focusEffectDisabled().Использование переменной FocusState также позволяет вам программно установить фокус на определенной кнопке, если вы этого захотите. Например, это может быть полезно для установки фокуса на кнопке ОК в диалоговом окне.
struct MyButtonStyle: ButtonStyle {
let isFocused: Bool
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding()
.frame(minWidth: 100)
.foregroundStyle(.black)
.fontWeight(isFocused ? .bold : .regular)
.border(.black)
.background(
Group {
isFocused ? Color.yellow : Color.white
}
.shadow(color: .black, radius: 4, x: 2, y: 2)
)
.animation(.easeInOut, value: isFocused)
}
}
struct ContentView: View {
enum ButtonPurpose {
case start
case reset
}
@FocusState private var isFocused: ButtonPurpose?
var body: some View {
HStack(spacing: 20) {
Button("Start") {}
.focused($isFocused, equals: .start)
.buttonStyle(MyButtonStyle(isFocused: isFocused == .start))
.focusEffectDisabled()
Button("Reset") {}
.focused($isFocused, equals: .reset)
.buttonStyle(MyButtonStyle(isFocused: isFocused == .reset))
}
}
}
Эффект фокуса отключен только на первой кнопке, поэтому его все еще можно увидеть на второй (с синим цветом акцента по умолчанию):

Вы можете использовать FocusState, чтобы отслеживать, какая кнопка в данный момент выбрана с помощью клавиатуры. Используя это, вы можете написать собственный ButtonStyle, который создает собственный эффект фокусировки, когда кнопка находится в фокусе. Вы можете отключить эффект фокусировки по умолчанию (синий контур), используя .focusEffectDisabled().
Передать состояние фокуса ButtonStyle немного сложно. Значение isFocusedсреды здесь как-то не работает. Вы можете изменить каждую кнопку индивидуально с помощью модификатора .buttonStyle с разными параметрами, как в ответе Бензи Низа, но я бы предпочел использовать собственное значение среды.
Например, вот стиль кнопки, который увеличивает кнопку, когда она находится в фокусе.
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 }
}
}
Вы можете использовать ViewModifier для передачи состояния фокуса:
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())
}
}
Пример использования:
HStack {
Button("Foo") { ... }
.customFocus()
Button("Bar") { ... }
.customFocus()
}
.buttonStyle(MyButtonStyle())
.focusEffectDisabled()