如何在 tvOS 上使用 SwiftUI 在多个水平列表之间导航

Gon*_*Oin 8 tvos swiftui

我在全局垂直列表中有多个可滚动水平列表,并且很难管理焦点更改。

焦点问题

我有两个主要问题:

  • 当一个视图下面没有另一个视图时,底部手势不会更新焦点。我希望焦点移动到下面的视图附近。在我的 GIF 示例中,从第 1 部分的第四个视图到第 2 部分的第二个视图。

  • 当某个视图正下方没有另一个视图时,底部手势会将焦点移动到垂直下方的视图。我希望焦点移动到最近的底部视图。在我的示例中,从第 1 节第三视图到第 2 节第二视图,而不是第 3 节第三视图。

@Environment(\.resetFocus) var resetFocus我在我的 tvOS 应用程序中有很多这个问题的例子,我通过使用 viewModifier和viewModifier的组合来解决这些问题prefersDefaultFocus,但我在这个用例上遇到了困难。

我还看到了关于这个问题的另一个类似的用例,但还没有答案。

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {

            Text("Section 1")

            ScrollView(.horizontal) {
                HStack() {
                    ForEach(0 ..< 10) { item in
                        Button(action: {
                            
                        }) {
                            FocusableRectangle()
                        }.buttonStyle(CardButtonStyle())
                    }
                }.padding(40)
            }
            
            Text("Section 2")

            ScrollView(.horizontal) {
                HStack() {
                    ForEach(0 ..< 2) { item in
                        Button(action: {
                            
                        }) {
                            FocusableRectangle()
                        }.buttonStyle(CardButtonStyle())
                    }
                }.padding(40)
            }
            
            Text("Section 3")

            ScrollView(.horizontal) {
                HStack() {
                    ForEach(0 ..< 3) { item in
                        Button(action: {
                            
                        }) {
                            FocusableRectangle()
                        }.buttonStyle(CardButtonStyle())
                    }
                }.padding(40)
            }
            
            Spacer()
        }
    }
}

struct FocusableRectangle: View {
    @Environment(\.isFocused) var isFocused: Bool
    @State var color = Color.blue
    
    var body: some View {
        Rectangle()
            .fill(color)
            .frame(width: 300.0, height: 200.0)
            .onChange(of: isFocused, perform: { value in
                color = value ? Color.red : Color.blue
            })
    }
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*tin 5

我想知道您是否尝试过.focusSection()在不同的 ScrollView 部分上添加与 tvOS 15 一起出现的内容?

这就是你的 ContentView 的样子:

struct ContentView: View {

var body: some View {
VStack(alignment: .leading) {
            Text("Section 1")

            ScrollView(.horizontal) {
                HStack {
                    ForEach(0..<10) { _ in
                        Button(action: {}) {
                            FocusableRectangle()
                        }.buttonStyle(CardButtonStyle())
                    }
                }.padding(40)
            }.focusSection()

            Text("Section 2")

            ScrollView(.horizontal) {
                HStack {
                    ForEach(0..<2) { _ in
                        Button(action: {}) {
                            FocusableRectangle()
                        }.buttonStyle(CardButtonStyle())
                    }
                }.padding(40)
            }.focusSection()

            Text("Section 3")

            ScrollView(.horizontal) {
                HStack {
                    ForEach(0..<3) { _ in
                        Button(action: {}) {
                            FocusableRectangle()
                        }.buttonStyle(CardButtonStyle())
                    }
                }.padding(40)
            }.focusSection()

            Spacer()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它应该能够达到目的并实现可理解的焦点模式。

希望能帮助到你。


小智 4

您需要将每个部分插入到容器中,例如GroupZStack。然后,您可以尝试覆盖.onMoveCommandwith.up.downinstructions 以设置当前选定的部分。现在,您可以.prefersDefaultFocus为每个部分设置条件。当选择发生变化时,您还需要调用resetFocus

struct ContentView: View {
    @Namespace var mainNamespace
    @Environment(\.resetFocus) var resetFocus
    
    @State private var selectedSection = SelectedSection.sec_1
    
    var body: some View {
        VStack(alignment: .leading) {

            Text("Section 1")

            Group(){
                ScrollView(.horizontal) {
                    HStack() {
                        ForEach(0 ..< 10) { item in
                            Button(action: {
                                
                            }) {
                                FocusableRectangle()
                            }.buttonStyle(CardButtonStyle())
                        }
                    }.padding(40)
                }
            }.onMoveCommand(){direction in
                print(direction)
                if direction == .down{
                    self.selectedSection = SelectedSection.sec_2
                }
            }.prefersDefaultFocus(self.selectedSection == SelectedSection.sec_1, in: mainNamespace)

            Text("Section 2")

            Group(){
                ScrollView(.horizontal) {
                    HStack() {
                        ForEach(0 ..< 2) { item in
                            Button(action: {
                                
                            }) {
                                FocusableRectangle()
                            }.buttonStyle(CardButtonStyle())
                        }
                    }.padding(40)
                }
            }.onMoveCommand(){direction in
                print(direction)
                if direction == .down{
                    self.selectedSection = SelectedSection.sec_3
                }
                if direction == .up{
                    self.selectedSection = SelectedSection.sec_1
                }
            }.prefersDefaultFocus(self.selectedSection == SelectedSection.sec_2, in: mainNamespace)

            Text("Section 3")

            Group(){
                ScrollView(.horizontal) {
                    HStack() {
                        ForEach(0 ..< 3) { item in
                            Button(action: {
                                
                            }) {
                                FocusableRectangle()
                            }.buttonStyle(CardButtonStyle())
                        }
                    }.padding(40)
                }
            }.onMoveCommand(){direction in
                print(direction)
                if direction == .up{
                    self.selectedSection = SelectedSection.sec_2
                }
            }.prefersDefaultFocus(self.selectedSection == SelectedSection.sec_3, in: mainNamespace)

            Spacer()
        }
        .focusScope(mainNamespace)
        .onAppear(){
            print(selectedSection)
        }
        .onChange(of: self.selectedSection){newValue in
            resetFocus(in: mainNamespace)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)