Build SwiftUI apps for tvOS
Description: Add a new dimension to your tvOS app with SwiftUI. We’ll show you how to build layouts powered by SwiftUI and customize your interface with custom buttons, provide more functionality in your app with a context menu, check if views are focused, and manage default focus. To get the most out of this session, you should be comfortable with SwiftUI. For a primer, watch “Introducing SwiftUI: Building Your First App” and “SwiftUI On All Devices.”
CardButtonStyle
- For tvOS-style buttons, use the new
CardButtonStyle
- raises when focused
- directional effects when dragging on the Siri remote
Button(albumLabel, action: playAlbum)
.buttonStyle(CardButtonStyle())
Context Menus
- invoked on long press gesture
AlbumView()
.contextMenu {
Button("Add to Favorites", action: addAlbumToFavorites)
Button("View Artist", action: viewArtistPage)
Button("Discover Similar Albums", action: viewSimilarAlbums)
}
Focus
- primary way to interact with a TV app
- incredibly important to be able to focus on views and determine if a view is focused
- use the
focusable(_:onFocusChange:)
modifier to make a view focusable - not meant for intrinsically focusable views (buttons, lists, ..)
- use the modifier
onFocusChange
parameter to configure your state - new
isFocused
Environment variable- lets you check whether or not a view is in focus, even if the view itself is not focusable
true
if the nearest focusable ancestor of your view is focused
Default focus
- tvOS will geometrically compute the view that should be focused on load
- this is typically the topmost or leading focusable view on the screen
- use
prefersDefaultFocus(_:in:)
to change the default behavior - use
focusScope(_:)
to limit your focus preferences to a specific view instead of globally
@Namespace private var namespace
@State private var areCredentialsFilled: Bool
var body: some View {
VStack {
TextField("Username", text: $username)
.prefersDefaultFocus(!areCredentialsFilled, in: namespace)
SecureField("Password", text: $password)
Button("Log In", action: logIn)
.prefersDefaultFocus(areCredentialsFilled, in: namespace)
}
.focusScope(namespace)
}
- use
resetFocus
environment action to reset the focus back to its default (limited to the current scope)
@Namespace var mainNamespace
@Environment(\.resetFocus) var resetFocus
var body: some View {
// ...
resetFocus(in: mainNamespace)
// ...
}