Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2020 SwiftUI & UI FrameworksAudio & Video

WWDC20 · 15 min · SwiftUI & UI Frameworks / Audio & Video

Build SwiftUI apps for tvOS

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.”

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 8 snippets

CardButtonStyle swift · at 1:42 ↗
Button(albumLabel, action: playAlbum)
    .buttonStyle(CardButtonStyle())
Custom Button Styles swift · at 2:24 ↗
struct MyNewButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
           .background(configuration.isPressed ?  : ) // Custom styling
    }
}           

Button(albumLabel, action: playAlbum)
    .buttonStyle(MyNewButtonStyle())
Context Menus swift · at 3:19 ↗
AlbumView()
    .contextMenu {
        Button("Add to Favorites", action: addAlbumToFavorites)
        Button("View Artist", action: viewArtistPage)
        Button("Discover Similar Albums", action: viewSimilarAlbums)
    }
isFocused Environment Variable swift · at 5:47 ↗
struct SongView: View {
    var body: some View {
        Button(action: playSong) {
            VStack {
                Image(albumArt)
                DetailsView(...)
            }
        }.buttonStyle(MyCustomButtonStyle())
    }
}

struct DetailsView: View {
    ...
    @Environment(\.isFocused) var isFocused: Bool
    var body: some View {
        VStack {
            Text(songName)
            Text(isFocused ? artistAndAlbum : artistName)
        }
    }
}
Login Screen (Default Focus) swift · at 8:42 ↗
var body: some View {
    VStack {
        TextField("Username", text: $username)

        SecureField("Password", text: $password)

        Button("Log In", action: logIn)

    }

}
Default Focus swift · at 8:51 ↗
@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)
}
Reset Focus swift · at 11:12 ↗
@Namespace private var namespace
@State private var areCredentialsFilled: Bool
@Environment(\.resetFocus) var resetFocus

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)

        Button("Clear", action: { 
            username = ""; password = ""
            areCredentialsFilled = false
            resetFocus(in: namespace)
        })
    }
    .focusScope(namespace)
}
Lazy Grids swift · at 12:45 ↗
struct ShelfView: View {
    var body: some View {
        ScrollView([.horizontal]) {
            LazyHGrid(rows: [GridItem()]) {
                ForEach(playlists, id: \.self) { playlist in                
                    Button(action: goToPlaylist) {
                        Image(playlist.coverImage)
                            .resizable()
                            .frame()
                    }
                    .buttonStyle(CardButtonStyle())
                }
            }
        }
    }
}

Resources