cYberSport91
cYberSport91

Reputation: 203

tvOS + SwiftUI Focusing between sections

I've set up a pretty simple SwiftUI tvOS app. I'm having a hard time with the Focus engine. When the app starts, it's focused on "Launch" which is understandable. Swiping down, goes to the StackView. Swiping left/right across the items works great. Except I can't swipe back up to get to Launch, no matter where I try it.

The goal is swiping up should always go up to the Launch button (focusing on the cells changes the content above)

Am I doing something glaringly wrong here? I'm under the impression I'm not doing anything special or complicated. Thanks

enter image description here

Here's my code:


struct ContentView: View {
    @ObservedObject private var viewModel = EnvironmentsViewModel()
    @State private var currentEnvironment: Environment?
    var environments: [Environment]?

    var body: some View {
        GeometryReader { geometry in
            VStack(spacing: 50) {
                BannerView(environment: currentEnvironment)
                    .frame(height: geometry.size.height * 0.6)
                    .clipped()

                ScrollView(.horizontal, showsIndicators: false) {
                    HStack(alignment: .center, spacing: 50) {
                        ForEach(environments ?? viewModel.environments) { environment in
                            Button(action: {
                                currentEnvironment = environment
                            }) {
                                EnvironmentCard(title: environment.title, subtitle: "Coming Soon", image: environment.image, isFocused: environment.title == currentEnvironment?.title)
                                    .frame(width: 500, height: geometry.size.height * 0.4)
                                    .edgesIgnoringSafeArea(.all)
                            }
                            .buttonStyle(PlainButtonStyle())
                        }
                    }
                }
                .padding(.leading, 50)
                .padding(.trailing, 50)
                .frame(height: geometry.size.height * 0.4)
            }
            .edgesIgnoringSafeArea([.top, .leading, .trailing])
            .onAppear {
                self.viewModel.fetch()
            }
        }
    }
}
struct BannerView: View {
    var environment: Environment?

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                if let environment = environment, let url = URL(string: environment.image) {
                    URLImage(url, placeholder: Image("Placeholder")) { proxy in
                        proxy.image
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                    }
                    .edgesIgnoringSafeArea(.all)
                        .frame(height: geometry.size.height)
                } else {
                    Color.darkGray.edgesIgnoringSafeArea(.all)
                }

                VStack(alignment: .leading, spacing: 10) {
                    Spacer()
                    Text(environment?.title ?? "Title")
                    Text("Description")
                    Button("Launch", action: {})
                }
                .frame(maxWidth: .infinity, alignment: .leading)
                .padding(.leading, 50).padding(.bottom, 50)
            }
            .frame(height: geometry.size.height)
        }
    }
}

Upvotes: 8

Views: 2444

Answers (1)

Shaybc
Shaybc

Reputation: 3177

the solution is to declare each line or level of focus (each focusable) as a focus section

just add: .focusSection() to the end of the HStack view you want to be a "focusable" line

read more here:

https://developer.apple.com/documentation/swiftui/view/focussection()

so in your code it should look like this:

struct ContentView: View {
    @ObservedObject private var viewModel = EnvironmentsViewModel()
    @State private var currentEnvironment: Environment?
    var environments: [Environment]?

    var body: some View {
        GeometryReader { geometry in
            VStack(spacing: 50) {
                BannerView(environment: currentEnvironment)
                    .frame(height: geometry.size.height * 0.6)
                    .clipped()
                    .focusSection() // <-------- ADD THIS ############

                ScrollView(.horizontal, showsIndicators: false) {
                    HStack(alignment: .center, spacing: 50) {
                        ForEach(environments ?? viewModel.environments) { environment in
                            Button(action: {
                                currentEnvironment = environment
                            }) {
                                EnvironmentCard(title: environment.title, subtitle: "Coming Soon", image: environment.image, isFocused: environment.title == currentEnvironment?.title)
                                    .frame(width: 500, height: geometry.size.height * 0.4)
                                    .edgesIgnoringSafeArea(.all)
                            }
                            .buttonStyle(PlainButtonStyle())
                        }
                    }
                }
                .padding(.leading, 50)
                .padding(.trailing, 50)
                .frame(height: geometry.size.height * 0.4)
                .focusSection() // <-------- ADD THIS ############
            }
            .edgesIgnoringSafeArea([.top, .leading, .trailing])
            .onAppear {
                self.viewModel.fetch()
            }
        }
    }
}

Upvotes: 0

Related Questions