在 SwiftUI (tvOS) 中获取按钮的 onFocusChange 回调

问题描述

onFocusChange 修饰符中的 focusable(_:onFocusChange:) 闭包允许我在子视图聚焦时为父视图设置属性,如下所示:

struct ContentView: View {
    @State var text: String
    var body: some View {
        vstack {
            Text(text)
            Text("top")
                .padding()
                .focusable(true,onFocusChange: { focused in
                    text = "top focus"
                })
            Text("bottom")
                .padding()
                .focusable(true,onFocusChange: { focused in
                    text = "bottom focus"
                })
        }
        
    }
}

但在 2020 年 WWDC 视频中,focusable 被引入,明确指出此包装器不打算用于本质上可聚焦的视图,例如按钮和列表。如果我在这里使用 Button 代替 Text,则 onFocusChange 有效,但 Buttons 的正常焦点行为会中断:

struct ContentView: View {
    @State var text: String
    var body: some View {
        vstack {
            Text(text)
            Button("top") {}
                .padding()
                .focusable(true,onFocusChange: { focused in
                    text = "top focus"
                })
            Button("bottom") {}
                .padding()
                .focusable(true,onFocusChange: { focused in
                    text = "bottom focus"
                })
        }
        
    }
}

有没有什么通用的方法可以让 onFocusChange 闭包与按钮一起使用,而不会破坏它们的正常可聚焦行为?或者有什么其他方法可以做到这一点?

解决方法

尝试在 ButtonStyle 中使用 Student@Environment(\.isFocused)

.onChange(of:perform:)

在 ButtonStyle 中使用 struct ContentView: View { var body: some View { Button("top") { // button action } .buttonStyle(MyButtonStyle()) } } struct MyButtonStyle: ButtonStyle { @Environment(\.isFocused) var focused: Bool func makeBody(configuration: Configuration) -> some View { configuration.label .onChange(of: focused) { newValue in // do whatever based on focus } } } 的 IIRC 可能仅适用于 iOS 14.5+,但您可以创建自定义视图而不是 ButtonStyle 以支持旧版本。