How do I use the focus-engine of tvOS with SwiftUI?

2.9k Views Asked by At

I have some focus-engine issues with tvOS and SwiftUI.

When I use buttons in SwiftUI, the focus engine handles it perfectly (the button is automatically focusable and it pops to the front with the fun animations when it is focussed). When I use VStacks and HStacks to create a grid with images, .focusable() does handle the focus part, but visually on screen there is nothing to see other than the grid is scrollable.

I really want my images in the grid to be highlighted and also pop to the front, like I easily can do in the TVML/TVJS (but who does not want to go native). I want to see if I can go all native, but Apple does very little on the tvOS part of their demos and documentation.

I have tried looking for example projects that take care of this, but I can't find anything. Maybe someone here knows. Many thanks in advance!

2

There are 2 best solutions below

0
On

To detect focus on a view in tvOS 15+ you should use the

.focused(, equals: )

method in conjunction with a @FocusState var. For instance to detect if a button is focused you could do this:

struct ContentView: View {

@FocusState var focusState: String?

var body: some View {
    Button("Button") { }
        .focused($focusState, equals: "button")
    }
}

This however just updates the focus state variable. If you want to respond to these updates you should use the onChange method:

.onChange(of: focusState) { newValue in
    // Handle focus changes.
}
1
On

Yes indeed there is not to many to find. This is what I've done so far:

struct DefaultCell: View {
    @State var focused: Bool = false

    var record : Record
    var body: some View {

        VStack{
            Image("placeholder")
            Text(record.title)
                .foregroundColor(self.focused ? .red : .gray)

        }
        .focusable(true, onFocusChange:{ (changed) in
            self.focused = changed
        })
    }
}

My Record looks as follows:

struct Record: Decodable, Hashable {
    var id:Int;
    let title: String;
    let poster_path,overview: String?;
}