And here is the working solution for tvOS 15:
struct ContentView: View {
@FocusState var focusedItem: String?
@State var items = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
let columns: [GridItem] = Array(repeating: .init(.flexible()), count: 4)
var body: some View {
ScrollView() {
LazyVGrid(columns: columns) {
ForEach(items, id: \.self) { item in
Button {
} label: {
ItemView(title: item)
}
.focused($focusedItem, equals: item)
.onMoveCommand { moveCommandDirection in
guard let index = items.firstIndex(where: {$0 == item}) else {
return
}
if moveCommandDirection == .down, index >= items.count - columns.count {
focusedItem = items.last
}
}
.padding(.bottom, 8)
}
}
}
}
}
If you have any workaround for tvOS 14 I'll be appreciated it.
Post
Replies
Boosts
Views
Activity
Yes, seems like since you wrap up HStack to ScrollView prefersDefaultFocus won't work correctly after that. I have a similar issue and can't get it to work for tvOS 14. Nevertheless, it works well with iOS 15 using @FocusState. So if you don't need tvOS 14 support you can use that approach. Here are my observations of the issue. What I try to achieve is set focus on the last item(10) if you press down on items 7 or 8. If I'm not wrapping up LazyVGrid into ScrollView everything works as expected. But as soon as I wrap up LazyVGrid into ScrollView when I press the down button on those items it always gets focused on the first item. Here is my code example and screenshot.
struct ContentView: View {
@Namespace var focusNamespace
@Environment(\.resetFocus) var resetFocus
@State private var defaultFocusIndex: Int = 0
@State var items = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
let columns: [GridItem] = Array(repeating: .init(.flexible()), count: 4)
var body: some View {
ScrollView() {
LazyVGrid(columns: columns) {
ForEach(items, id: \.self) { item in
Button {
} label: {
ItemView(title: item)
}
.prefersDefaultFocus(item == items[defaultFocusIndex], in: focusNamespace)
.onMoveCommand { moveCommandDirection in
guard let index = items.firstIndex(where: {$0 == item}) else {
return
}
if moveCommandDirection == .down, index >= items.count - columns.count {
defaultFocusIndex = items.count - 1
resetFocus(in: focusNamespace)
}
}
}
}
.focusScope(focusNamespace)
}
}
}