Reputation: 29
I am having a hard time trying to figured out how to focus on a specific cell/row in the list in the SwiftUI 2.0 and tvOS 14. I need to be able to focus and select a specific record when I am navigated to a view. However when the focus is switched to the list, some random row is focused. I've tried ScrollView and List to create a list of items with Buttons as items and with appropriate prefersDefaultFocus. Nothing works. Here's some sample code:
struct ChannelListView: View {
@Namespace private var namespace
@ObservedObject var viewModel : LiveViewModel
@State var selection = Set<ChannelItem>()
var body: some View {
List(viewModel.channels, selection: $selection){ item in
ScrollViewReader { proxy in
Button(action: {
}){
ChannelItemView(item: item, selectedItem: $viewModel.selectedChannel, onSelected: { id in
})
.padding(.vertical, 2)
}
.buttonStyle(ChannelButtonStyle())
.prefersDefaultFocus(item == viewModel.selectedChannel, in: namespace)
}
}
.focusScope(namespace)
}
}
Upvotes: 1
Views: 1622
Reputation: 511
Found a way to achieve it. Using the defaultFocus
modifier does the trick. You need to supply the desired element from the list as its argument, plus you need to specify the priority as .userInitiated
, without it, it doesn't work correctly.
Plus, we track which was the last focused (aka remembersLastFocusedIndexPath
from UIKit
)
@main
struct FocusApp: App {
var body: some Scene {
WindowGroup {
VStack(spacing: 50) {
CustomRow()
CustomRow()
}
}
}
}
struct CustomRow: View {
let items = [1, 2, 3, 4]
@FocusState private var currentFocusItem: Int?
@State private var lastFocusItem: Int?
var body: some View {
ScrollView(.horizontal) {
LazyHStack(spacing: 50) {
ForEach(items, id: \.self) { item in
Button { } label: {
Text("Item \(item)")
.frame(width: 300, height: 300, alignment: .center)
.background(Color.red)
}
.focused($currentFocusItem, equals: item)
}
}
}
.defaultFocus($currentFocusItem, lastFocusItem ?? items.first, priority: .userInitiated)
.onChange(of: currentFocusItem) {
if currentFocusItem != nil {
lastFocusItem = currentFocusItem
}
}
}
}
Upvotes: 0