Reputation: 203
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
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
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