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
 

Vidéos

Ouvrir le menu Fermer le menu
  • Collections
  • Toutes les vidéos
  • À propos

Plus de vidéos

  • À propos
  • Code
  • What’s new in SwiftUI

    Learn how you can use SwiftUI to build great apps for any Apple platform. Explore a fresh new look and feel for tabs and documents on iPadOS. Improve your window management with new windowing APIs, and gain more control over immersive spaces and volumes in your visionOS apps. We'll also take you through other exciting refinements that help you make expressive charts, customize and layout text, and so much more.

    Chapitres

    • 0:00 - Introduction
    • 0:51 - Fresh apps
    • 1:04 - Fresh apps: TabView
    • 2:22 - Fresh apps: Presentation sizing
    • 2:39 - Fresh apps: Zoom transition
    • 3:02 - Fresh apps: Custom controls
    • 3:38 - Fresh apps: Vectorized and function plots
    • 4:10 - Fresh apps: TableColumnForEach
    • 4:25 - Fresh apps: MeshGradient
    • 4:51 - Fresh apps: Document launch experience
    • 5:33 - Fresh apps: SF Symbols 6
    • 6:37 - Harnessing the platform
    • 6:52 - Harnessing the platform: Windowing
    • 8:28 - Harnessing the platform: Input methods
    • 10:45 - Harnessing the platform: Widgets and Live Activities
    • 12:25 - Intermezzo
    • 12:55 - Framework foundations
    • 13:09 - Framework foundations: Custom containers
    • 13:48 - Framework foundations: Ease of use
    • 16:18 - Framework foundations: Scrolling enhancements
    • 17:18 - Framework foundations: Swift 6 language mode
    • 18:01 - Framework foundations: Improved interoperability
    • 19:18 - Crafting experiences
    • 19:43 - Crafting experiences: Volumes
    • 20:27 - Crafting experiences: Immersive spaces
    • 21:27 - Crafting experiences: TextRenderer
    • 22:12 - Next steps

    Ressources

    • SwiftUI updates
    • Forum: UI Frameworks
      • Vidéo HD
      • Vidéo SD

    Vidéos connexes

    WWDC24

    • Bring your Live Activity to Apple Watch
    • Catch up on accessibility in SwiftUI
    • Create custom hover effects in visionOS
    • Create custom visual effects with SwiftUI
    • Demystify SwiftUI containers
    • Dive deep into volumes and immersive spaces
    • Elevate your tab and sidebar experience in iPadOS
    • Enhance your UI animations and transitions
    • Evolve your document launch experience
    • Extend your app’s controls across the system
    • Migrate your app to Swift 6
    • Squeeze the most out of Apple Pencil
    • Swift Charts: Vectorized and function plots
    • Tailor macOS windows with SwiftUI
    • What’s new in SF Symbols 6
    • Work with windows in SwiftUI
  • Rechercher dans cette vidéo…
    • 1:38 - TabView

      import SwiftUI
      
      struct KaraokeTabView: View {
          @State var customization = TabViewCustomization()
          
          var body: some View {
              TabView {
                  Tab("Parties", image: "party.popper") {
                      PartiesView(parties: Party.all)
                  }
                  .customizationID("karaoke.tab.parties")
                  
                  Tab("Planning", image: "pencil.and.list.clipboard") {
                      PlanningView()
                  }
                  .customizationID("karaoke.tab.planning")
      
                  Tab("Attendance", image: "person.3") {
                      AttendanceView()
                  }
                  .customizationID("karaoke.tab.attendance")
      
                  Tab("Song List", image: "music.note.list") {
                      SongListView()
                  }
                  .customizationID("karaoke.tab.songlist")
              }
              .tabViewStyle(.sidebarAdaptable)
              .tabViewCustomization($customization)
          }
      }
      
      struct PartiesView: View {
          var parties: [Party]
          var body: some View { Text("PartiesView") }
      }
      
      struct PlanningView: View {
          var body: some View { Text("PlanningView") }
      }
      
      struct AttendanceView: View {
          var body: some View { Text("AttendanceView") }
      }
      
      struct SongListView: View {
          var body: some View { Text("SongListView") }
      }
      
      struct Party {
          static var all: [Party] = []
      }
      
      #Preview {
          KaraokeTabView()
      }
    • 2:28 - Presentation sizing

      import SwiftUI
      
      struct AllPartiesView: View {
          @State var showAddSheet: Bool = true
          var parties: [Party] = []
          
          var body: some View {
              PartiesGridView(parties: parties, showAddSheet: $showAddSheet)
                  .sheet(isPresented: $showAddSheet) {
                      AddPartyView()
                          .presentationSizing(.form)
                  }
          }
      }
      
      struct PartiesGridView: View {
          var parties: [Party]
          @Binding var showAddSheet: Bool
          
          var body: some View {
              Text("PartiesGridView")
          }
      }
      
      struct AddPartyView: View {
          var body: some View {
              Text("AddPartyView")
          }
      }
      
      struct Party {
          static var all: [Party] = []
      }
      
      #Preview {
          AllPartiesView()
      }
    • 2:39 - Zoom transition

      import SwiftUI
      
      struct PartyView: View {
          var party: Party
          @Namespace() var namespace
          
          var body: some View {
              NavigationLink {
                  PartyDetailView(party: party)
                      .navigationTransition(.zoom(
                          sourceID: party.id, in: namespace))
              } label: {
                  Text("Party!")
              }
              .matchedTransitionSource(id: party.id, in: namespace)
          }
      }
      
      struct PartyDetailView: View {
          var party: Party
          
          var body: some View {
              Text("PartyDetailView")
          }
      }
      
      struct Party: Identifiable {
          var id = UUID()
          static var all: [Party] = []
      }
      
      #Preview {
          @Previewable var party: Party = Party()
          NavigationStack {
              PartyView(party: party)
          }
      }
    • 3:18 - Controls API

      import WidgetKit
      import SwiftUI
      
      struct StartPartyControl: ControlWidget {
          var body: some ControlWidgetConfiguration {
              StaticControlConfiguration(
                  kind: "com.apple.karaoke_start_party"
              ) {
                  ControlWidgetButton(action: StartPartyIntent()) {
                      Label("Start the Party!", systemImage: "music.mic")
                      Text(PartyManager.shared.nextParty.name)
                  }
              }
          }
      }
      
      // Model code
      
      class PartyManager {
          static let shared = PartyManager()
          var nextParty: Party = Party(name: "WWDC Karaoke")
      }
      
      struct Party {
          var name: String
      }
      
      // AppIntent
      
      import AppIntents
      
      struct StartPartyIntent: AppIntent {
          static let title: LocalizedStringResource = "Start the Party"
          
          func perform() async throws -> some IntentResult {
              return .result()
          }
      }
    • 3:49 - Function plotting

      import SwiftUI
      import Charts
      
      struct AttendanceView: View {
          var body: some View {
              Chart {
                LinePlot(x: "Parties", y: "Guests") { x in
                  pow(x, 2)
                }
                .foregroundStyle(.purple)
              }
              .chartXScale(domain: 1...10)
              .chartYScale(domain: 1...100)
          }
      }
      
      #Preview {
          AttendanceView()
              .padding(40)
      }
    • 4:18 - Dynamic table columns

      import SwiftUI
      
      struct SongCountsTable: View {
      
          var body: some View {
              Table(Self.guestData) {
                  // A static column for the name
                  TableColumn("Name", value: \.name)
                  
                  TableColumnForEach(Self.partyData) { party in
                      TableColumn(party.name) { guest in
                          Text(guest.songsSung[party.id] ?? 0, format: .number)
                      }
                  }
              }
          }
          
          private static func randSongsSung(low: Bool = false) -> [Int : Int] {
              var songs: [Int : Int] = [:]
              for party in partyData {
                  songs[party.id] = low ? Int.random(in: 0...3) : Int.random(in: 3...12)
              }
              return songs
          }
          
          private static let guestData: [GuestData] = [
              GuestData(name: "Sommer", songsSung: randSongsSung()),
              GuestData(name: "Sam", songsSung: randSongsSung()),
              GuestData(name: "Max", songsSung: randSongsSung()),
              GuestData(name: "Kyle", songsSung: randSongsSung(low: true)),
              GuestData(name: "Matt", songsSung: randSongsSung(low: true)),
              GuestData(name: "Apollo", songsSung: randSongsSung()),
              GuestData(name: "Anna", songsSung: randSongsSung()),
              GuestData(name: "Raj", songsSung: randSongsSung()),
              GuestData(name: "John", songsSung: randSongsSung(low: true)),
              GuestData(name: "Harry", songsSung: randSongsSung()),
              GuestData(name: "Luca", songsSung: randSongsSung()),
              GuestData(name: "Curt", songsSung: randSongsSung()),
              GuestData(name: "Betsy", songsSung: randSongsSung())
          ]
          
          private static let partyData: [PartyData] = [
              PartyData(partyNumber: 1, numberGuests: 5),
              PartyData(partyNumber: 2, numberGuests: 6),
              PartyData(partyNumber: 3, numberGuests: 7),
              PartyData(partyNumber: 4, numberGuests: 9),
              PartyData(partyNumber: 5, numberGuests: 9),
              PartyData(partyNumber: 6, numberGuests: 10),
              PartyData(partyNumber: 7, numberGuests: 11),
              PartyData(partyNumber: 8, numberGuests: 12),
              PartyData(partyNumber: 9, numberGuests: 11),
              PartyData(partyNumber: 10, numberGuests: 13),
          ]
          
      }
      
      struct GuestData: Identifiable {
          let name: String
          let songsSung: [Int : Int]
          
          let id = UUID()
      }
      
      struct PartyData: Identifiable {
          let partyNumber: Int
          let numberGuests: Int
          let symbolSize = 100
          
          var id: Int {
              partyNumber
          }
          
          var name: String {
              "\(partyNumber)"
          }
      }
      
      #Preview {
          SongCountsTable()
              .padding(40)
      }
    • 4:42 - Mesh gradients

      import SwiftUI
      
      struct MyMesh: View {
          var body: some View {
              MeshGradient(
                  width: 3,
                  height: 3,
                  points: [
                      .init(0, 0), .init(0.5, 0), .init(1, 0),
                      .init(0, 0.5), .init(0.3, 0.5), .init(1, 0.5),
                      .init(0, 1), .init(0.5, 1), .init(1, 1)
                  ],
                  colors: [
                      .red, .purple, .indigo,
                      .orange, .cyan, .blue,
                      .yellow, .green, .mint
                  ]
              )
          }
      }
      
      #Preview {
          MyMesh()
              .statusBarHidden()
      }
    • 5:14 - Document launch scene

      DocumentGroupLaunchScene("Your Lyrics") {
          NewDocumentButton()
          Button("New Parody from Existing Song") {
              // Do something!
          }
      } background: {
          PinkPurpleGradient()
      } backgroundAccessoryView: { geometry in
          MusicNotesAccessoryView(geometry: geometry)
               .symbolEffect(.wiggle(.rotational.continuous()))
      } overlayAccessoryView: { geometry in
          MicrophoneAccessoryView(geometry: geometry)
      }
    • 7:04 - Window styling and default placement

      Window("Lyric Preview", id: "lyricPreview") {
          LyricPreview()
      }
        .windowStyle(.plain)
        .windowLevel(.floating)
        .defaultWindowPlacement { content, context in
            let displayBounds = context.defaultDisplay.visibleRect
            let contentSize = content.sizeThatFits(.unspecified)
            return topPreviewPlacement(size: contentSize, bounds: displayBounds)
        }
      }
    • 7:30 - Window Drag Gesture

      Text(currentLyric)
          .background(.thinMaterial, in: .capsule)
          .gesture(WindowDragGesture())
    • 8:18 - Push window environment action

      struct EditorView: View {
          @Environment(\.pushWindow) private var pushWindow
          
          var body: some View {
              Button("Play", systemImage: "play.fill") {
                  pushWindow(id: "lyric-preview")
              }
          }
      }
    • 8:47 - Hover effects

      struct ProfileButtonStyle: ButtonStyle {
          func makeBody(configuration: Configuration) -> some View {
              configuration.label
                  .background(.thinMaterial)
                  .hoverEffect(.highlight)
                  .clipShape(.capsule)
                  .hoverEffect { effect, isActive, _ in
                      effect.scaleEffect(isActive ? 1.05 : 1.0)
                  }
          }
      }
    • 9:14 - Modifier key alternates

      Button("Preview Lyrics in Window") {
          // show preview in window
      }
      .modifierKeyAlternate(.option) {
          Button("Preview Lyrics in Full Screen") {
              // show preview in full screen
          }
      }
      .keyboardShortcut("p", modifiers: [.shift, .command])
    • 9:32 - Responding to modifier keys

      var body: some View {
              LyricLine()
                  .overlay(alignment: .top) {
                      if showBouncingBallAlignment {
                          // Show bouncing ball alignment guide
                      }
                  }
                  .onModifierKeysChanged(mask: .option) {
                      showBouncingBallAlignment = !$1.isEmpty
                  }
          }
    • 9:55 - Pointer customization

      ForEach(resizeAnchors) { anchor in
          ResizeHandle(anchor: anchor)
               .pointerStyle(.frameResize(position: anchor.position))
      }
    • 10:23 - Pencil squeeze gesture

      @Environment(\.preferredPencilSqueezeAction) var preferredAction
          
      var body: some View {
          LyricsEditorView()
              .onPencilSqueeze { phase in
                  if preferredAction == .showContextualPalette, case let .ended(value) = phase {
                      if let anchorPoint = value.hoverPose?.anchor {
                          lyricDoodlePaletteAnchor = .point(anchorPoint)
                      }
                      lyricDoodlePalettePresented = true
                  }
             }
    • 13:13 - Custom containers

      struct DisplayBoard<Content: View>: View {
        @ViewBuilder var content: Content
      
        var body: some View {
          DisplayBoardCardLayout {
            ForEach(subviewOf: content) { subview in
              CardView {
                subview
              }
            }
          }
          .background { BoardBackgroundView() }
        }
      }
      
      DisplayBoard {
        Text("Scrolling in the Deep")
        Text("Born to Build & Run")
        Text("Some Body Like View")
      
        ForEach(songsFromSam) { song in
          Text(song.title)
        }
      }
    • 13:35 - Custom containers with sectioning

      DisplayBoard {
        Section("Matt's Favorites") {
          Text("Scrolling in the Deep")
          Text("Born to Build & Run")
          Text("Some Body Like View")
            .displayBoardCardRejected(true)
        }
      	Section("Sam's Favorites") {
          ForEach(songsFromSam) { song in
            Text(song.title)
          }
        }
      }
    • 13:52 - Entry macro

      extension EnvironmentValues {
        @Entry var karaokePartyColor: Color = .purple
      }
      
      extension FocusValues {
        @Entry var lyricNote: String? = nil
      }
      
      extension Transaction {
        @Entry var animatePartyIcons: Bool = false
      }
      
      extension ContainerValues {
        @Entry var displayBoardCardStyle: DisplayBoardCardStyle = .bordered
      }
    • 14:12 - Default accessibility label augmentation

      SongView(song)
        .accessibilityElement(children: .combine)
        .accessibilityLabel { label in
          if let rating = song.rating {
            Text(rating)
          }
          label
        }
    • 14:52 - Previewable

      #Preview {
         @Previewable @State var showAllSongs = true
         Toggle("Show All songs", isOn: $showAllSongs)
      }
    • 15:06 - Programatic text selection

      struct LyricView: View {
        @State private var selection: TextSelection?
        
        var body: some View {
          TextField("Line \(line.number)", text: $line.text, selection: $selection)
          // ...
        }
      }
    • 15:19 - Getting selected ranges

      InspectorContent(text: line.text, ranges: selection?.ranges)
    • 15:29 - Binding to search field focus state

      // Binding to search field focus state
      
      struct SongSearchView: View {
        @FocusState private var isSearchFieldFocused: Bool
        
        @State private var searchText = ""
        @State private var isPresented = false
      
        var body: some View {
          NavigationSplitView {
            Text("Power Ballads")
            Text("Show Tunes")
          } detail: {
            // ...
            if !isSearchFieldFocused {
              Button("Find another song") {
                isSearchFieldFocused = true
              }
            }
          }
          .searchable(text: $searchText, isPresented: $isPresented)
          .searchFocused($isSearchFieldFocused)
        }
      }
    • 15:41 - Text suggestions

      TextField("Line \(line.number)", text: $line.text)
        .textInputSuggestions {
          ForEach(lyricCompletions) {
            Text($0.attributedCompletion)
              .textInputCompletion($0.text)
          }
        }
    • 15:59 - Color mixing

      Color.red.mix(with: .purple, by: 0.2)
      Color.red.mix(with: .purple, by: 0.5)
      Color.red.mix(with: .purple, by: 0.8)
    • 16:13 - Custom shaders

      ContentView()
        .task {
          let slimShader = ShaderLibrary.slim()
          try! await slimShader.compile(as: .layerEffect)
        }
    • 16:23 - React to scroll geometry changes

      struct ContentView: View {
        @State private var showBackButton = false
      
        ScrollView {
          // ...
        }
        .onScrollGeometryChange(for: Bool.self) { geometry in
          geometry.contentOffset.y < geometry.contentInsets.top
        } action: { wasScrolledToTop, isScrolledToTop in
          withAnimation {
            showBackButton = !isScrolledToTop
          }
        }
      }
    • 16:42 - React to scroll visibility changes

      struct AutoPlayingVideo: View {
        @State private var player: AVPlayer = makePlayer()
      
        var body: some View {
          VideoPlayer(player: player)
            .onScrollVisibilityChange(threshold: 0.2) { visible in
              if visible {
                player.play()
              } else {
                player.pause()
              }
            }
        }
      }
    • 16:54 - New scroll positions

      struct ContentView: View {
        @State private var position: ScrollPosition =
          .init(idType: Int.self)
      
        var body: some View {
          ScrollView {
            // ... 
          }
          .scrollPosition($position)
          .overlay {
            FloatingButton("Back to Invitation") {
              position.scrollTo(edge: .top)
            }
          }
        }
      }
    • 18:17 - Gesture interoperability

      struct VideoThumbnailScrubGesture: UIGestureRecognizerRepresentable {
        @Binding var progress: Double
      
        func makeUIGestureRecognizer(context: Context) -> VideoThumbnailScrubGestureRecognizer {
          VideoThumbnailScrubGestureRecognizer()
        }
      
        func handleUIGestureRecognizerAction(
          _ recognizer: VideoThumbnailScrubGestureRecognizer, context: Context
        ) {
          progress = recognizer.progress
        }
      }
      
      struct VideoThumbnailTile: View {
        var body: some View {
          VideoThumbnail()
            .gesture(VideoThumbnailScrubGesture(progress: $progress))
        }
      }
    • 18:34 - SwiftUI animations in UIKit and AppKit

      let animation = SwiftUI.Animation.spring(duration: 0.8)
      
      // UIKit
      UIView.animate(animation) {
          view.center = endOfBracelet
      }
      
      // AppKit
      NSAnimationContext.animate(animation) {
          view.center = endOfBracelet
      }
    • 18:57 - Representable animation bridging

      struct BeadBoxWrapper: UIViewRepresentable {
        @Binding var isOpen: Bool
      
        func updateUIView(_ box: BeadBox, context: Context) {
      		context.animate {
      		  box.lid.center.y = isOpen ? -100 : 100
      		}
        }
      }
    • 19:59 - Volume baseplate visibility

      struct KaraokePracticeApp: App {
        var body: some Scene {
          WindowGroup {
            ContentView()
          }
          .windowStyle(.volumetric)
          .defaultWorldScaling(.trueScale)
          .volumeBaseplateVisibility(.hidden)
        }
      }
    • 20:15 - React to volume viewpoint changes

      struct MicrophoneView: View {
       @State var micRotation: Rotation3D = .identity
          
        var body: some View {
          Model3D(named: "microphone")
            .onVolumeViewpointChange { _, new in
              micRotation = rotateToFace(new)
            }
            .rotation3DEffect(micRotation)
            .animation(.easeInOut, value: micRotation)
        } 
      }
    • 20:38 - Control allowed immersion levels

      struct KaraokeApp: App {
        @State private var immersion: ImmersionStyle = .progressive(
          0.4...1.0, initialAmount: 0.5)
        
        var body: some Scene {
          ImmersiveSpace(id: "Karaoke") {
            LoungeView()
          }
          .immersionStyle(selection: $immersion, in: immersion)
         }
      }
    • 21:00 - Preferred surrounding effects

      struct LoungeView: View {
        var body: some View {
          StageView()
            .preferredSurroundingsEffect(.colorMultiply(.purple))
        }
      }
    • 21:33 - Custom text renderers

      struct KaraokeRenderer: TextRenderer {
        func draw(
          layout: Text.Layout,
          in context: inout GraphicsContext
        ) {
          for line in layout {
            for run in line {
              var glow = context
      
              glow.addFilter(.blur(radius: 8))
              glow.addFilter(purpleColorFilter)
      
              glow.draw(run)
              context.draw(run)
            }
          }
        }
      }
      
      struct LyricsView: View {
        var body: some View {
          Text("A Whole View World")
            .textRenderer(KaraokeRenderer())
        }
      }
      
      #Preview {
        LyricsView()
      }

Developer Footer

  • Vidéos
  • WWDC24
  • 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