View in English

  • Apple Developer
    • Get Started

    Explore Get Started

    • Overview
    • Learn
    • Apple Developer Program

    Stay Updated

    • Latest News
    • Hello Developer
    • Platforms

    Explore Platforms

    • Apple Platforms
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store

    Featured

    • Design
    • Distribution
    • Games
    • Accessories
    • Web
    • Home
    • CarPlay
    • Technologies

    Explore Technologies

    • Overview
    • Xcode
    • Swift
    • SwiftUI

    Featured

    • Accessibility
    • App Intents
    • Apple Intelligence
    • Games
    • Machine Learning & AI
    • Security
    • Xcode Cloud
    • Community

    Explore Community

    • Overview
    • Meet with Apple events
    • Community-driven events
    • Developer Forums
    • Open Source

    Featured

    • WWDC
    • Swift Student Challenge
    • Developer Stories
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Centers
    • Documentation

    Explore Documentation

    • Documentation Library
    • Technology Overviews
    • Sample Code
    • Human Interface Guidelines
    • Videos

    Release Notes

    • Featured Updates
    • iOS
    • iPadOS
    • macOS
    • watchOS
    • visionOS
    • tvOS
    • Xcode
    • Downloads

    Explore Downloads

    • All Downloads
    • Operating Systems
    • Applications
    • Design Resources

    Featured

    • Xcode
    • TestFlight
    • Fonts
    • SF Symbols
    • Icon Composer
    • Support

    Explore Support

    • Overview
    • Help Guides
    • Developer Forums
    • Feedback Assistant
    • Contact Us

    Featured

    • Account Help
    • App Review Guidelines
    • App Store Connect Help
    • Upcoming Requirements
    • Agreements and Guidelines
    • System Status
  • Quick Links

    • Events
    • News
    • Forums
    • Sample Code
    • Videos
 

Videos

Abrir menú Cerrar menú
  • Colecciones
  • Todos los videos
  • Información

Más videos

  • Información
  • Código
  • What's new in SwiftUI

    SwiftUI can help you build better and more powerful apps for iPhone, iPad, Mac, Apple Watch, and Apple TV. Learn more about the latest refinements to SwiftUI, including interface improvements like outlines, grids, and toolbars. Take advantage of SwiftUI's enhanced support across Apple frameworks to enable features like Sign In with Apple. Discover new visual effects, as well as new controls and styles. And find out how the new app and scene APIs enable you to create apps entirely in SwiftUI, as well as custom complications and all new widgets.

    To get the most out of this session, you should be familiar with SwiftUI. Watch "Introduction to SwiftUI" for a primer.

    Recursos

    • SwiftUI
      • Video HD
      • Video SD

    Videos relacionados

    WWDC20

    • App essentials in SwiftUI
    • Build complications in SwiftUI
    • Build document-based apps in SwiftUI
    • Build SwiftUI apps for tvOS
    • Build SwiftUI views for widgets
    • Data Essentials in SwiftUI
    • Introduction to SwiftUI
    • Meet WidgetKit
    • Optimize the interface of your Mac Catalyst app
    • SF Symbols 2
    • Stacks, Grids, and Outlines in SwiftUI
    • The details of UI typography
    • What's new in Mac Catalyst
    • What's new in Swift
    • Widgets Code-along, part 1: The adventure begins
    • Widgets Code-along, part 2: Alternate timelines
    • Widgets Code-along, part 3: Advancing timelines
  • Buscar este video…
    • 1:26 - Hello World

      @main
      struct HelloWorld: App {
          var body: some Scene {
              WindowGroup {
                  Text("Hello, world!").padding()
              }
          }
      }
    • 1:56 - Book Club app

      @main
      struct BookClubApp: App {
          @StateObject private var store = ReadingListStore()
      
          var body: some Scene {
              WindowGroup {
                  ReadingListViewer(store: store)
              }
          }
      }
      
      struct ReadingListViewer: View {
          @ObservedObject var store: ReadingListStore
      
          var body: some View {
              NavigationView {
                  List(store.books) { book in
                      Text(book.title)
                  }
                  .navigationTitle("Currently Reading")
              }
          }
      }
      
      class ReadingListStore: ObservableObject {
          init() {}
      
          var books = [
              Book(title: "Book #1", author: "Author #1"),
              Book(title: "Book #2", author: "Author #2"),
              Book(title: "Book #3", author: "Author #3")
          ]
      }
      
      struct Book: Identifiable {
          let id = UUID()
          let title: String
          let author: String
      }
    • 4:46 - Settings

      @main
      struct BookClubApp: App {
          @StateObject private var store = ReadingListStore()
      
          @SceneBuilder var body: some Scene {
              WindowGroup {
                  ReadingListViewer(store: store)
              }
              
          #if os(macOS)
              Settings {
                  BookClubSettingsView()
              }
          #endif
          }
      }
      
      struct BookClubSettingsView: View {
          var body: some View {
              Text("Add your settings UI here.")
                  .padding()
          }
      }
      
      struct ReadingListViewer: View {
          @ObservedObject var store: ReadingListStore
      
          var body: some View {
              NavigationView {
                  List(store.books) { book in
                      Text(book.title)
                  }
                  .navigationTitle("Currently Reading")
              }
          }
      }
      
      class ReadingListStore: ObservableObject {
          init() {}
      
          var books = [
              Book(title: "Book #1", author: "Author #1"),
              Book(title: "Book #2", author: "Author #2"),
              Book(title: "Book #3", author: "Author #3")
          ]
      }
      
      struct Book: Identifiable {
          let id = UUID()
          let title: String
          let author: String
      }
    • 5:10 - Document groups

      import SwiftUI
      import UniformTypeIdentifiers
      
      @main
      struct ShapeEditApp: App {
          var body: some Scene {
              DocumentGroup(newDocument: ShapeDocument()) { file in
                  DocumentView(document: file.$document)
              }
          }
      }
      
      struct DocumentView: View {
          @Binding var document: ShapeDocument
          
          var body: some View {
              Text(document.title)
                  .frame(width: 300, height: 200)
          }
      }
      
      struct ShapeDocument: Codable {
          var title: String = "Untitled"
      }
      
      extension UTType {
          static let shapeEditDocument =
              UTType(exportedAs: "com.example.ShapeEdit.shapes")
      }
      
      extension ShapeDocument: FileDocument {
          static var readableContentTypes: [UTType] { [.shapeEditDocument] }
          
          init(fileWrapper: FileWrapper, contentType: UTType) throws {
              let data = fileWrapper.regularFileContents!
              self = try JSONDecoder().decode(Self.self, from: data)
          }
      
          func write(to fileWrapper: inout FileWrapper, contentType: UTType) throws {
              let data = try JSONEncoder().encode(self)
              fileWrapper = FileWrapper(regularFileWithContents: data)
          }
      }
    • 5:49 - Custom Commands

      import SwiftUI
      import UniformTypeIdentifiers
      
      @main
      struct ShapeEditApp: App {
          var body: some Scene {
              DocumentGroup(newDocument: ShapeDocument()) { file in
                  DocumentView(document: file.$document)
              }
              .commands {
                  CommandMenu("Shapes") {
                      Button("Add Shape...", action: addShape)
                          .keyboardShortcut("N")
                      Button("Add Text", action: addText)
                          .keyboardShortcut("T")
                  }
              }
          }
          
          func addShape() {}
          func addText() {}
      }
      
      struct DocumentView: View {
          @Binding var document: ShapeDocument
          
          var body: some View {
              Text(document.title)
                  .frame(width: 300, height: 200)
          }
      }
      
      struct ShapeDocument: Codable {
          var title: String = "Untitled"
      }
      
      extension UTType {
          static let shapeEditDocument =
              UTType(exportedAs: "com.example.ShapeEdit.shapes")
      }
      
      extension ShapeDocument: FileDocument {
          static var readableContentTypes: [UTType] { [.shapeEditDocument] }
          
          init(fileWrapper: FileWrapper, contentType: UTType) throws {
              let data = fileWrapper.regularFileContents!
              self = try JSONDecoder().decode(Self.self, from: data)
          }
      
          func write(to fileWrapper: inout FileWrapper, contentType: UTType) throws {
              let data = try JSONEncoder().encode(self)
              fileWrapper = FileWrapper(regularFileWithContents: data)
          }
      }
    • 7:55 - Widgets

      import SwiftUI
      import WidgetKit
      
      @main
      struct RecommendedAlbum: Widget {
          var body: some WidgetConfiguration {
              StaticConfiguration(
                  kind: "RecommendedAlbum",
                  provider: Provider(),
                  placeholder: PlaceholderView()
              ) { entry in
                  AlbumWidgetView(album: entry.album)
              }
              .configurationDisplayName("Recommended Album")
              .description("Your recommendation for the day.")
          }
      }
      
      struct AlbumWidgetView: View {
          var album: Album
      
          var body: some View {
              Text(album.title)
          }
      }
      
      struct PlaceholderView: View {
          var body: some View {
              Text("Placeholder View")
          }
      }
      
      struct Album {
          var title: String
      }
      
      struct Provider: TimelineProvider {
          struct Entry: TimelineEntry {
              var album: Album
              var date: Date
          }
      
          public func snapshot(with context: Context, completion: @escaping (Entry) -> ()) {
              let entry = Entry(album: Album(title: "Untitled"), date: Date())
              completion(entry)
          }
      
          public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
              var entries: [Entry] = []
      
              // Generate a timeline consisting of five entries an hour apart, starting from the current date.
              let currentDate = Date()
              for hourOffset in 0 ..< 5 {
                  let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
                  let entry = Entry(album: Album(title: "Untitled #\(hourOffset)"), date: entryDate)
                  entries.append(entry)
              }
      
              let timeline = Timeline(entries: entries, policy: .atEnd)
              completion(timeline)
          }
      }
    • 8:31 - Complications using SwiftUI

      struct CoffeeHistoryChart: View {
          var body: some View {
              VStack {
                  ComplicationHistoryLabel {
                      Text("Weekly Coffee")
                          .complicationForeground()
                  }
                  HistoryChart()
              }
              .complicationChartFont()
          }
      }
      
      struct ComplicationHistoryLabel: View { ... }
      struct HistoryChart: View { ... }
      
      extension View {
          func complicationChartFont() -> some View { ... }
      }
    • 9:22 - Outlines

      struct OutlineContentView: View {
          var graphics: [Graphic]
          
          var body: some View {
              List(graphics, children: \.children) { graphic in
                  GraphicRow(graphic)
              }
              .listStyle(SidebarListStyle())
          }
      }
      
      struct Graphic: Identifiable {
          var id: String
          var name: String
          var icon: Image
          var children: [Graphic]?
      }
      
      struct GraphicRow: View {
          var graphic: Graphic
          
          init(_ graphic: Graphic) {
              self.graphic = graphic
          }
          
          var body: some View {
              Label {
                  Text(graphic.name)
              } icon: {
                  graphic.icon
              }
          }
      }
    • 10:09 - Adaptive grids

      struct ContentView: View {
          var items: [Item]
      
          var body: some View {
              ScrollView {
                  LazyVGrid(columns: [GridItem(.adaptive(minimum: 176))]) {
                      ForEach(items) { item in
                          ItemView(item: item)
                      }
                  }
                  .padding()
              }
          }
      }
      
      struct Item: Identifiable {
          var name: String
          var id = UUID()
          
          var icon: Image {
              Image(systemName: name)
          }
          var color: Color {
              colors[colorIndex % (colors.count - 1)]
          }
      
          private static var nextColorIndex: Int = 0
          private var colorIndex: Int
      
          init(name: String) {
              self.name = name
      
              colorIndex = Self.nextColorIndex
              Self.nextColorIndex += 1
          }
      }
      
      struct ItemView: View {
          var item: Item
      
          var body: some View {
              ZStack {
                  RoundedRectangle(cornerRadius: 8, style: .continuous)
                      .fill()
                      .layoutPriority(1)
                      .foregroundColor(item.color)
                  item.icon
                      .resizable()
                      .aspectRatio(contentMode: .fit)
                      .padding(.all, 16)
                      .foregroundColor(.white)
              }
              .frame(width: 176, height: 110)
          }
      }
    • 10:28 - Fixed-column grids

      struct ContentView: View {
          var items: [Item]
      
          var body: some View {
              ScrollView {
                  LazyVGrid(columns: Array(repeating: GridItem(), count: 4)]) {
                      ForEach(items) { item in
                          ItemView(item: item)
                      }
                  }
                  .padding()
              }
          }
      }
      
      struct Item: Identifiable {
          var name: String
          var id = UUID()
          
          var icon: Image {
              Image(systemName: name)
          }
          var color: Color {
              colors[colorIndex % (colors.count - 1)]
          }
      
          private static var nextColorIndex: Int = 0
          private var colorIndex: Int
      
          init(name: String) {
              self.name = name
      
              colorIndex = Self.nextColorIndex
              Self.nextColorIndex += 1
          }
      }
      
      struct ItemView: View {
          var item: Item
      
          var body: some View {
              ZStack {
                  RoundedRectangle(cornerRadius: 8, style: .continuous)
                      .fill()
                      .layoutPriority(1)
                      .foregroundColor(item.color)
                  item.icon
                      .resizable()
                      .aspectRatio(contentMode: .fit)
                      .padding(.all, 16)
                      .foregroundColor(.white)
              }
              .frame(width: 176, height: 110)
          }
      }
    • 10:38 - Horizontal grids

      struct ContentView: View {
          var items: [Item]
      
          var body: some View {
              ScrollView(.horizontal) {
                  LazyHGrid(rows: [GridItem(.adaptive(minimum: 110))]) {
                      ForEach(items) { item in
                          ItemView(item: item)
                      }
                  }
                  .padding()
              }
          }
      }
      
      struct Item: Identifiable {
          var name: String
          var id = UUID()
          
          var icon: Image {
              Image(systemName: name)
          }
          var color: Color {
              colors[colorIndex % (colors.count - 1)]
          }
      
          private static var nextColorIndex: Int = 0
          private var colorIndex: Int
      
          init(name: String) {
              self.name = name
              colorIndex = Self.nextColorIndex
              Self.nextColorIndex += 1
          }
      }
      
      struct ItemView: View {
          var item: Item
      
          var body: some View {
              ZStack {
                  RoundedRectangle(cornerRadius: 8, style: .continuous)
                      .fill()
                      .layoutPriority(1)
                      .foregroundColor(item.color)
                  item.icon
                      .resizable()
                      .aspectRatio(contentMode: .fit)
                      .padding(.all, 16)
                      .foregroundColor(.white)
              }
              .frame(width: 176, height: 110)
          }
      }
    • 10:58 - Lazy stacks

      struct WildlifeList: View {
          var rows: [ImageRow]
      
          var body: some View {
              ScrollView {
                  LazyVStack(spacing: 2) {
                      ForEach(rows) { row in
                          switch row.content {
                          case let .singleImage(image):
                              SingleImageLayout(image: image)
                          case let .imageGroup(images):
                              ImageGroupLayout(images: images)
                          case let .imageRow(images):
                              ImageRowLayout(images: images)
                          }
                      }
                  }
              }
          }
      }
    • 12:24 - Toolbar modifier

      struct ContentView: View {
          var body: some View {
              List {
                  Text("Book List")
              }
              .toolbar {
                  Button(action: recordProgress) {
                      Label("Record Progress", systemImage: "book.circle")
                  }
              }
          }
      
          private func recordProgress() {}
      }
    • 12:40 - ToolbarItem

      struct ContentView: View {
          var body: some View {
              List {
                  Text("Book List")
              }
              .toolbar {
                  ToolbarItem(placement: .primaryAction) {
                      Button(action: recordProgress) {
                          Label("Record Progress", systemImage: "book.circle")
                      }
                  }
              }
          }
      
          private func recordProgress() {}
      }
    • 12:47 - Confirmation and cancellation toolbar placements

      struct ContentView: View {
          var body: some View {
              Form {
                  Slider(value: .constant(0.39))
              }
              .toolbar {
                  ToolbarItem(placement: .confirmationAction) {
                      Button("Save", action: saveProgress)
                  }
                  ToolbarItem(placement: .cancellationAction) {
                      Button("Cancel", action: dismissSheet)
                  }
              }
          }
      
          private func saveProgress() {}
          private func dismissSheet() {}
      }
    • 13:00 - Principal toolbar placement

      struct ContentView: View {
          enum ViewMode {
              case details
              case notes
          }
      
          @State private var viewMode: ViewMode = .details
      
          var body: some View {
              List {
                  Text("Book Detail")
              }
              .toolbar {
                  ToolbarItem(placement: .principal) {
                      Picker("View", selection: $viewMode) {
                          Text("Details").tag(ViewMode.details)
                          Text("Notes").tag(ViewMode.notes)
                      }
                  }
              }
          }
      }
    • 13:17 - Bottom bar toolbar placement

      struct ContentView: View {
          var body: some View {
              List {
                  Text("Book Detail")
              }
              .toolbar {
                  ToolbarItem {
                      Button(action: recordProgress) {
                          Label("Progress", systemImage: "book.circle")
                      }
                  }
                  ToolbarItem(placement: .bottomBar) {
                      Button(action: shareBook) {
                          Label("Share", systemImage: "square.and.arrow.up")
                      }
                  }
              }
          }
      
          private func recordProgress() {}
          private func shareBook() {}
      }
    • 13:38 - Label

      Label("Progress", systemImage: "book.circle")
    • 14:06 - Label expanded form

      Label {
          Text("Progress")
      } icon: {
          Image(systemName: "book.circle")
      }
    • 14:24 - Bottom bar toolbar placement

      struct ContentView: View {
          var body: some View {
              List {
                  Text("Book Detail")
              }
              .toolbar {
                  ToolbarItem {
                      Button(action: recordProgress) {
                          Label("Progress", systemImage: "book.circle")
                      }
                  }
                  ToolbarItem(placement: .bottomBar) {
                      Button(action: shareBook) {
                          Label("Share", systemImage: "square.and.arrow.up")
                      }
                  }
              }
          }
      
          private func recordProgress() {}
          private func shareBook() {}
      }
    • 14:36 - Context menu Labels

      struct ContentView: View {
          var body: some View {
              List {
                  Text("Book List Row")
                  .contextMenu {
                      Button(action: recordProgress) {
                          Label("Progress", systemImage: "book.circle")
                      }
                      Button(action: addToFavorites) {
                          Label("Add to Favorites", systemImage: "heart")
                      }
                      Button(action: shareBook) {
                          Label("Share", systemImage: "square.and.arrow.up")
                      }
                  }
              }
          }
      
          private func recordProgress() {}
          private func addToFavorites() {}
          private func shareBook() {}
      }
    • 14:39 - List Labels

      struct ContentView: View {
          var body: some View {
              List {
                  Group {
                      Label("Introducing SwiftUI", systemImage: "hand.wave")
                      Label("SwiftUI Essentials", systemImage: "studentdesk")
                      Label("Data Essentials in SwiftUI", systemImage: "flowchart")
                      Label("App Essentials in SwiftUI", systemImage: "macwindow.on.rectangle")
                  }
                  Group {
                      Label("Build Document-based apps in SwiftUI", systemImage: "doc")
                      Label("Stacks, Grids, and Outlines", systemImage: "list.bullet.rectangle")
                      Label("Building Custom Views in SwiftUI", systemImage: "sparkles")
                      Label("Build SwiftUI Apps for tvOS", systemImage: "tv")
                      Label("Build SwiftUI Views for Widgets", systemImage: "square.grid.2x2.fill")
                      Label("Create Complications for Apple Watch", systemImage: "gauge")
                      Label("SwiftUI on All Devices", systemImage: "laptopcomputer.and.iphone")
                      Label("Integrating SwiftUI", systemImage: "rectangle.connected.to.line.below")
                  }
              }
          }
      }
    • 15:28 - Help modifier

      struct ContentView: View {
          var body: some View {
              Button(action: recordProgress) {
                  Label("Progress", systemImage: "book.circle")
              }
              .help("Record new progress entry")
          }
      
          private func recordProgress() {}
      }
    • 16:12 - Keyboard shortcut modifier

      @main
      struct BookClubApp: App {
          var body: some Scene {
              WindowGroup {
                  List {
                      Text("Reading List Viewer")
                  }
              }
              .commands {
                  Button("Previous Book", action: selectPrevious)
                      .keyboardShortcut("[")
                  Button("Next Book", action: selectNext)
                      .keyboardShortcut("]")
              }
          }
      
          private func selectPreviousBook() {}
          private func selectNextBook() {}
      }
    • 16:28 - Cancel and default action keyboard shortcuts

      struct ContentView: View {
          var body: some View {
              HStack {
                  Button("Cancel", action: dismissSheet)
                      .keyboardShortcut(.cancelAction)
      
                  Button("Save", action: saveProgress)
                      .keyboardShortcut(.defaultAction)
              }
          }
      
          private func dismissSheet() {}
          private func saveProgress() {}
      }
    • 17:08 - ProgressView

      struct ContentView: View {
          var percentComplete: Double
      
          var body: some View {
              ProgressView("Downloading Photo", value: percentComplete)
          }
      }
    • 17:19 - Circular ProgressView

      struct ContentView: View {
          var percentComplete: Double
      
          var body: some View {
              ProgressView("Downloading Photo", value: percentComplete)
                  .progressViewStyle(CircularProgressViewStyle())
          }
      }
    • 17:25 - Activity indicator ProgressView

      struct ContentView: View {
          var body: some View {
              ProgressView()
          }
      }
    • 17:32 - Gauge

      struct ContentView: View {
          var acidity: Double
      
          var body: some View {
              Gauge(value: acidity, in: 3...10) {
                  Label("Soil Acidity", systemImage: "drop.fill")
                      .foregroundColor(.green)
              }
          }
      }
    • 17:52 - Gauge with current value label

      struct ContentView: View {
          var acidity: Double
      
          var body: some View {
              Gauge(value: acidity, in: 3...10) {
                  Label("Soil Acidity", systemImage: "drop.fill")
                      .foregroundColor(.green)
              } currentValueLabel: {
                  Text("\(acidity, specifier: "%.1f")")
              }
          }
      }
    • 18:00 - Gauge with minimum and maximum value labels

      struct ContentView: View {
          var acidity: Double
      
          var body: some View {
              Gauge(value: acidity, in: 3...10) {
                  Label("Soil Acidity", systemImage: "drop.fill")
                      .foregroundColor(.green)
              } currentValueLabel: {
                  Text("\(acidity, specifier: "%.1f")")
              } minimumValueLabel: {
                  Text("3")
              } maximumValueLabel: {
                  Text("10")
              }
          }
      }
    • 18:57 - Initial Album Picker

      struct ContentView: View {
          @State private var selectedAlbumIDs: Set<Album.ID> = []
      
          var body: some View {
              VStack(spacing: 0) {
                  ScrollView {
                      albumGrid.padding(.horizontal)
                  }
      
                  Divider().zIndex(-1)
      
                  selectedAlbumRow
                      .frame(height: AlbumCell.albumSize)
                      .padding(.top, 8)
              }
              .buttonStyle(PlainButtonStyle())
          }
      
          private var albumGrid: some View {
              LazyVGrid(columns: [GridItem(.adaptive(minimum: AlbumCell.albumSize))], spacing: 8) {
                 ForEach(unselectedAlbums) { album in
                    Button(action: { select(album) }) {
                       AlbumCell(album)
                    }
                 }
              }
          }
      
          private var selectedAlbumRow: some View {
              HStack {
                  ForEach(selectedAlbums) { album in
                      AlbumCell(album)
                  }
              }
          }
      
          private var unselectedAlbums: [Album] {
              Album.allAlbums.filter { !selectedAlbumIDs.contains($0.id) }
          }
          private var selectedAlbums: [Album] {
              Album.allAlbums.filter { selectedAlbumIDs.contains($0.id) }
          }
      
          private func select(_ album: Album) {
              withAnimation(.spring(response: 0.5)) {
                  _ = selectedAlbumIDs.insert(album.id)
              }
          }
      }
      
      struct AlbumCell: View {
          static let albumSize: CGFloat = 100
      
          var album: Album
      
          init(_ album: Album) {
              self.album = album
          }
      
          var body: some View {
              album.image
                  .frame(width: AlbumCell.albumSize, height: AlbumCell.albumSize)
                  .background(Color.pink)
                  .cornerRadius(6.0)
          }
      }
      
      struct Album: Identifiable {
          static let allAlbums: [Album] = [
              .init(name: "Sample", image: Image(systemName: "music.note")),
              .init(name: "Sample 2", image: Image(systemName: "music.note.list")),
              .init(name: "Sample 3", image: Image(systemName: "music.quarternote.3")),
              .init(name: "Sample 4", image: Image(systemName: "music.mic")),
              .init(name: "Sample 5", image: Image(systemName: "music.note.house")),
              .init(name: "Sample 6", image: Image(systemName: "tv.music.note"))
          ]
      
          var name: String
          var image: Image
      
          var id: String { name }
      }
    • 19:17 - Matched geometry effect Album Picker

      struct ContentView: View {
          @Namespace private var namespace
          @State private var selectedAlbumIDs: Set<Album.ID> = []
      
          var body: some View {
              VStack(spacing: 0) {
                  ScrollView {
                      albumGrid.padding(.horizontal)
                  }
      
                  Divider().zIndex(-1)
      
                  selectedAlbumRow
                      .frame(height: AlbumCell.albumSize)
                      .padding(.top, 8)
              }
              .buttonStyle(PlainButtonStyle())
          }
      
          private var albumGrid: some View {
              LazyVGrid(columns: [GridItem(.adaptive(minimum: AlbumCell.albumSize))], spacing: 8) {
                 ForEach(unselectedAlbums) { album in
                    Button(action: { select(album) }) {
                       AlbumCell(album)
                    }
                    .matchedGeometryEffect(id: album.id, in: namespace)
                 }
              }
          }
      
          private var selectedAlbumRow: some View {
              HStack {
                  ForEach(selectedAlbums) { album in
                      AlbumCell(album)
                      .matchedGeometryEffect(id: album.id, in: namespace)
                  }
              }
          }
      
          private var unselectedAlbums: [Album] {
              Album.allAlbums.filter { !selectedAlbumIDs.contains($0.id) }
          }
          private var selectedAlbums: [Album] {
              Album.allAlbums.filter { selectedAlbumIDs.contains($0.id) }
          }
      
          private func select(_ album: Album) {
              withAnimation(.spring(response: 0.5)) {
                  _ = selectedAlbumIDs.insert(album.id)
              }
          }
      }
      
      struct AlbumCell: View {
          static let albumSize: CGFloat = 100
      
          var album: Album
      
          init(_ album: Album) {
              self.album = album
          }
      
          var body: some View {
              album.image
                  .frame(width: AlbumCell.albumSize, height: AlbumCell.albumSize)
                  .background(Color.pink)
                  .cornerRadius(6.0)
          }
      }
      
      struct Album: Identifiable {
          static let allAlbums: [Album] = [
              .init(name: "Sample", image: Image(systemName: "music.note")),
              .init(name: "Sample 2", image: Image(systemName: "music.note.list")),
              .init(name: "Sample 3", image: Image(systemName: "music.quarternote.3")),
              .init(name: "Sample 4", image: Image(systemName: "music.mic")),
              .init(name: "Sample 5", image: Image(systemName: "music.note.house")),
              .init(name: "Sample 6", image: Image(systemName: "tv.music.note"))
          ]
      
          var name: String
          var image: Image
      
          var id: String { name }
      }
    • 19:53 - Container Relative Shape

      struct AlbumWidgetView: View {
          var album: Album
      
          var body: some View {
              album.image
                  .clipShape(ContainerRelativeShape())
                  .padding()
          }
      }
      
      struct Album {
          var name: String
          var artist: String
          var image: Image
      }
    • 20:34 - Dynamic Type scaling

      struct ContentView: View {
          var album: Album
          @ScaledMetric private var padding: CGFloat = 10
      
          var body: some View {
              VStack {
                  Text(album.name)
                      .font(.custom("AvenirNext-Bold", size: 30))
      
                  Text("\(Image(systemName: "music.mic")) \(album.artist)")
                      .font(.custom("AvenirNext-Bold", size: 17))
      
              }
              .padding(padding)
              .background(RoundedRectangle(cornerRadius: 16, style: .continuous).fill(Color.purple))
          }
      }
      
      struct Album {
          var name: String
          var artist: String
          var image: Image
      }
    • 22:08 - Initial Sidebar List

      struct ContentView: View {
          var body: some View {
              NavigationView {
                  List {
                      Label("Menu", systemImage: "list.bullet")
      
                      Label("Favorites", systemImage: "heart")
      
                      Label("Rewards", systemImage: "seal")
      
                      Section(header: Text("Recipes")) {
                          ForEach(1..<4) {
                              Label("Recipes \($0)", systemImage: "book.closed")
                          }
                      }
                  }
                  .listStyle(SidebarListStyle())
              }
          }
      }
    • 22:17 - List Item Tint in Sidebars

      struct ContentView: View {
          var body: some View {
              NavigationView {
                  List {
                      Label("Menu", systemImage: "list.bullet")
      
                      Label("Favorites", systemImage: "heart")
                          .listItemTint(.red)
      
                      Label("Rewards", systemImage: "seal")
                          .listItemTint(.purple)
      
                      Section(header: Text("Recipes")) {
                          ForEach(1..<4) {
                              Label("Recipes \($0)", systemImage: "book.closed")
                          }
                      }
                      .listItemTint(.monochrome)
                  }
                  .listStyle(SidebarListStyle())
              }
          }
      }
    • 22:33 - List Item Tint on watchOS

      struct ContentView: View {
          var body: some View {
              NavigationView {
                  List {
                      Label("Menu", systemImage: "list.bullet")
      
                      Label("Favorites", systemImage: "heart")
                          .listItemTint(.red)
      
                      Label("Rewards", systemImage: "seal")
                          .listItemTint(.purple)
      
                      Section(header: Text("Recipes")) {
                          ForEach(1..<4) {
                              Label("Recipes \($0)", systemImage: "book.closed")
                          }
                      }
                      .listItemTint(.monochrome)
                  }
              }
          }
      }
    • 22:46 - SwitchToggleStyle tint

      struct ContentView: View {
          @State var order = Order()
      
          var body: some View {
              Toggle("Send notification when ready", isOn: $order.notifyWhenReady)
                  .toggleStyle(SwitchToggleStyle(tint: .accentColor))
          }
      }
      
      struct Order {
          var notifyWhenReady = true
      }
    • 23:15 - Link

      let appleURL = URL(string: "https://developer.apple.com/tutorials/swiftui/")!
      let wwdcAnnouncementURL = URL(string: "https://apple.news/AjriX1CWUT-OfjXu_R4QsnA")!
      
      struct ContentView: View {
          var body: some View {
              Form {
                  Section {
                      Link(destination: apple) {
                          Label("SwiftUI Tutorials", systemImage: "swift")
                      }
                      Link(destination: wwdcAnnouncementURL) {
                          Label("WWDC 2020 Announcement", systemImage: "chevron.left.slash.chevron.right")
                      }
                  }
              }
          }
      }
    • 23:56 - OpenURL Environment Action

      let customPublisher = NotificationCenter.default.publisher(for: .init("CustomURLRequestNotification"))
      let apple = URL(string: "https://developer.apple.com/tutorials/swiftui/")!
      
      struct ContentView: View {
          @Environment(\.openURL) private var openURL
      
          var body: some View {
              Text("OpenURL Environment Action")
                  .onReceive(customPublisher) { output in
                      if output.userInfo!["shouldOpenURL"] as! Bool {
                          openURL(apple)
                      }
                  }
          }
      }
    • 24:44 - Uniform Type Identifiers

      import UniformTypeIdentifiers
      
      extension UTType {
          static let myFileFormat = UTType(exportedAs: "com.example.myfileformat")
      }
      
      func introspecContentType(_ fileURL: URL) throws {
          // Get this file's content type.
          let resourceValues = try fileURL.resourceValues(forKeys: [.contentTypeKey])
          if let type = resourceValues.contentType {
              // Get the human presentable description of the type.
              let description = type.localizedDescription
      
              if type.conforms(to: .myFileFormat) {
                  // The file is our app’s format.
              } else if type.conforms(to: .image) {
                  // The file is an image.
              }
          }
      }
    • 25:16 - Sign in with Apple Button

      import AuthenticationServices
      import SwiftUI
      
      struct ContentView: View {
          var body: some View {
              SignInWithAppleButton(
                  .signUp,
                  onRequest: handleRequest,
                  onCompletion: handleCompletion
              )
              .signInWithAppleButtonStyle(.black)
          }
      
          private func handleRequest(request: ASAuthorizationAppleIDRequest) {}
          private func handleCompletion(result: Result<ASAuthorization, Error>) {}
      }

Developer Footer

  • Videos
  • WWDC20
  • What's new in SwiftUI
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • Apple Intelligence
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Downloads
    • Sample Code
    • Videos
    Open Menu Close Menu
    • Help Guides & Articles
    • Contact Us
    • Forums
    • Feedback & Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • Mini Apps Partner Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Read the latest news.
    Get the Apple Developer app.
    Copyright © 2026 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines