Streaming is available in most browsers,
and in the Developer app.
-
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.
Chapters
- 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
Resources
Related Videos
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
-
Download
Hi, and thanks for tuning in! I’m Sommer! And I’m Sam! We’re both engineers on the SwiftUI team. And we’re excited to tell you all about what’s new in SwiftUI.
To help with our karaoke habit, Sam and I have been working on an app to plan our regular team karaoke parties.
Our app harnesses so many improvements to SwiftUI, and we're excited to share those improvements with you! Starting with plenty of great features to make your apps feel brand new. Tools to refine your apps so they feel at home on every platform. Widespread improvements to the foundational building blocks of the framework. And a whole suite of new tools for crafting immersive experiences. Whew! That’s a lot to cover, so let’s dive right in. You can really freshen up your apps with SwiftUI, from a new tab view, to beautiful mesh gradients, and snappy controls! Sam and I wrote a karaoke event planner app. It’s primarily a side bar driven app, and in iOS 18.0, the sidebar has become a lot more flexible. With just the touch of a button, our main-view changes to a tab bar representation for showing more of that beautiful UI. The tab bar now floats above the content! People can even customize the experience completely to their liking, reordering the items and hiding infrequently used options.
It was easy to rewrite my main view to use the new tabview.
Now, TabView has a new type-safe syntax in SwiftUI to make it easier to catch common errors at build time. And, as your content grows, it’s simple to make your tab view more flexible. By just applying the new .sidebarAdaptable tab view style, karaoke planners can now switch between the tab bar and sidebar view.
The sidebar looks great when you have a lot of content, like all these song lists, and it even has customization behavior like reordering and removing tabs, which is completely programmatically controllable. The refreshed side bar also looks great on tvOS, and you can style the tab view to appear as a sidebar or as a segmented control in the toolbar on macOS! Sheet presentation sizing is now unified and simplified across platforms. You can use the .presentationSizing modifier to create perfectly-sized sheets with .form or .page, or even use custom sizing. SwiftUI supports a new zoom navigation transition too, and I can use it to make expanding info on a party look gorgeous.
For more on the new TabView check out "Improve your tab and sidebar experience on iPad". And to dive deeper into new animations watch "Enhance your UI animations and transitions".
You can now create your own custom resizable controls such as buttons and toggles that live in control center or the lock screen and can even be activated by the action button. Controls are a new kind of Widget that that are easy to build with App Intents! With just a few lines of code, I can create a ControlWidgetButton to get the WWDC karaoke party started at a moment’s notice! To learn how to use the new and powerful controls API to customize your configurable buttons and toggles, check out: "Access your app's controls across the system." Sam and I have been working hard to grow our karaoke party attendance. I think an exponential function seems like very reasonable goal.
Function plotting in Swift charts makes it easy for me draw beautiful graphs, like this LinePlot.
I’ll go ahead and graph the actual attendance too.
Oof, not quite there yet.
To dig deeper into the improvements in Swift Charts, catch "Swift Charts: Vectorized and function plots".
As a data nerd, I also like to keep track of how much the attendees are singing at the parties.
Using TableColumnForEach, I can now have a dynamic number of table columns for however many parties Sam and I have! To boost our attendance numbers, I think we should send out some colorful party invites! SwiftUI has added first class support for colorful mesh gradients. By interpolating between the points on a grid of colors, you can create a beautiful lattice! This sounds like the perfect way make our karaoke invites as snazzy as our parties! Now that the Karaoke Planner App is looking fresh, Sam and I want to have a little extra fun by customizing our lyrics! So, we’ve also built a document-based app for editing the words to our favorite songs. I crafted this launch screen to express my app’s individuality and highlight its features using the new Document Launch Scene type.
I created a big bold title, customized the background and added some fun accessory views to really make my launch experience pop! To learn more about all you can do with document based apps, like custom document icons and templates watch “Evolve your document launch experience” I’m going to put a finishing touch on my launch screen now, using a symbol effect on my music notes! Wow, my symbols really do wiggle wiggle.
Apps can now adopt three new animation presets for SF Symbols. The wiggle effect oscillates a symbol in any direction or angle to draw attention.
The breathe effect smoothly scales a symbol up and down to indicate ongoing activity. And the rotate effect spins some parts of a symbol around a designated anchor point.
Some of the existing presets have also been enhanced with new features. For example, the default Replace animation now prefers a new MagicReplace behavior. With MagicReplace, symbols smoothly animate badges and slashes.
These are just a few of the enhancements in SF Symbols 6. To learn more watch “What’s new in SF Symbols”.
SwiftUI brings great improvements to making your apps feel at home on any of Apple’s platforms. With improved windowing, more control over input and lots of glanceable content, your apps can take advantage of whatever platform they’re on. You can now tailor the style and behavior of windows on macOS.
In my lyric editor app on macOS, I have a window that shows a single line preview. I used the new plain window style to remove the default window chrome. And gave it a floating window level so it shows on top of any other windows. I used the defaultWindowPlacement API to place it at the top of the screen, so it doesn't cover-up the rest of my lyrics; I took into account the size of the display, and the size of the content, to place it in the perfect spot.
I also added a WindowDragGesture to the content view of the preview so I can adjust its position on screen by dragging it around.
There are new scene types too, like utility window! To learn more about ways to customize the style and behavior of your windows, watch “Tailor macOS windows with SwiftUI!”.
This multi-window lyrics editor app works great on visionOS too! Recently my fellow karaoke pal Andrew showed me how he’s using this new push window action in the Botanist app to help him keep his most important content focused! pushWindow can be used to open a window and hide the originating window. I definitely want to do the same thing in my Lyrics editor, so I’ll use the pushWindow environment action to focus on my lyric preview. Learn more in the video: "Work with windows in SwiftUI". SwiftUI gives you many new tools for taking advantage of the unique input methods each platform offers.
In visionOS, you can make views react when people look at them, place a finger near them, or move the pointer over them, all while preserving people’s privacy.
Within the new closure-based hoverEffect modifier, I can control how the view looks as it transitions between the active and IN-active states To learn how you can also coordinate multiple effects, control the timing of an effect, and respond to accessibility settings, check out “Create custom hover effects in visionOS.” Great iPadOS, macOS, and visionOS apps provide great keyboard support.
In the main menu on macOS, I have an item to open my preview window.
I've added the new modifierKeyAlternate modifier, and on press of the option key I can reveal a secondary item to preview in full-screen.
For low level control, any view can respond to modifier key press state changes. I updated the lyric editor to use onModifierKeysChanged. So when I hold down option, I can see and adjust an extra alignment guide for where the bouncing ball lands on the lyrics.
Pointer interaction is another important form of input across many devices.
The pointerStyle API lets you customize the appearance and visibility of the system pointer. Since I made my lyrics resizable, I’ll apply an appropriate frameResize pointer style on each resize anchor.
New in iPadOS 17.5, SwiftUI has support for features of Apple Pencil and Apple Pencil Pro like double-tap and squeeze.
With .onPencilSqueeze, I can gather information from the gesture and see what action is preferred. In this case, I’m going to show my lyrics doodle palette under the pencil’s hover location so I can mark up lyrics with fun drawings. To learn more about all the new Apple Pencil APIs, head over to "Squeeze the most out of Apple Pencil". Widgets offer glanceable information and key interactions with your apps. And now that Live Activities have also come to watchOS, your iOS based live activities will automatically show up on Apple Watch, without any work on your part! Sam and I already have this live activity to review song lyrics on the go, and I can see it showing up automatically on my Apple Watch! To make it look really amazing, I can use the new small .supplementalActivityFamily to tailor the content for watchOS, showing more lyrics at once. Nice! And to allow our singers to advance the lyrics using double tap, I can apply the .handGestureShortcut modifier.
I also want to be sure that I always know when I’m up next at a karaoke event, so I added a widget to my Planner app using the new reference date format style to show a countdown to Karaoke o’ clock! Text now has additional formats for the display of live times and dates that work great in widgets and live activities! These formats include date references, date offsets and timers. Each format is deeply customizable down to its components, and can even adapt to the size of its container.
Widgets are smarter now too. Specify relevant contexts so the system can more intelligently surface them in places like smart stacks. With this, my countdown widget can show up automatically at a specified time, or when a singer gets close to a planned karaoke venue! Hey Sommer, How’s the lyrics editor coming along? I’ve been thinking up lyrics for my next solo, "Cupertino Dreamin", and I’ve gotta get it written down before I forget! It’s going great! I just used it to write some lyrics for “Smells Like Scene Spirit”, it’s gonna rock. Nice, we can try to slot that in towards the end of the setlist. That reminds me: the setlist! Is it gonna be ready for the WWDC karaoke party? You bet Sommer. The theme for the songs is all of SwiftUI’s great new Framework foundations. SwiftUI has added all sorts of new API that make working with the framework feel easier than ever, Including improvements to the core components of SwiftUI, new ways to use foundational APIs, and ease of use enhancements.
You can now create your own custom container views. A new API on ForEach, subviewOf, lets you to iterate over the subviews of a given view, like in this example where I'm wrapping each subview in its own card view. You can use this to make custom containers that have the same capabilities as SwiftUI’s built in containers like list and picker, including mixing static, and dynamic content supporting sections, and adding container specific modifiers. To learn more about custom containers and the SwiftUI fundamentals behind them, be sure to check out "Demystify SwiftUI Containers". New ease of use improvements make working with SwiftUI easier than ever. Instead of having to write out a full conformance to EnvironmentKey, and an extension on environment values, you can now just write a simple property with the new Entry macro. Best of all, it doesn’t just work with environment values. The entry macro can also be used with FocusValues, Transaction, and the new ContainerValues as well.
You can now attach additional information to SwiftUI's built in accessibility labels. That means you can add additional accessibility information to a control, without overriding the framework provided label. Be sure to check out "Catch up on accessibility in SwiftUI" to learn about all of SwiftUI’s amazing new accessibility features, such as conditional modifier support, and app intent based accessibilityActions.
Xcode previews has a new dynamic linking architecture that allows you to switch between a preview, and build-and-run without needing to re-build your project, increasing your iteration speed.
And it’s now easier to set up Previews too. You can now use State directly in previews using the Previewable macro, eliminating the boilerplate of wrapping your preview content in a view.
There are now new ways to work with text and manage selection. SwiftUI now offers programatic access to, and control of text selection within text editing controls! The contents of my selection binding update to match the selected text of the lyric fields.
Now I can read properties of the selection, such as the selected ranges. I can use this to show suggested rhymes for selected words in the inspector.
With .searchFocused, you can programmatically drive the focus state of a search field, meaning you can check if a search field is focused on, and programmatically move focus to and from the search field.
You can now add text suggestions to any text field. I'll use this to provide suggestions for how to finish a line. The suggestions appear as a drop down menu, and when I pick an option my textfield updates with the selected completion.
SwiftUI also has new graphics capabilities.
You can now beautifully mix colors together. A new mix modifier on color blends it with another Color by a given amount. We’ve also extended the custom shader feature with the ability to precompile shaders before their first use, so you can avoid frame drops caused by lazy shader compilation.
There are a bunch of new APIs to give you fine grained control of your scroll views. You can now have deeper level of integration with the state of a ScrollView with .onScrollGeometryChange, which lets you performantly react to changes in things like content offsets, content size and more. Like with this "back to invitation" button, which appears when I scroll past the top of the scroll view's contents.
You can now detect when a view’s visibility has changed due to scrolling! Letting you create great experiences centered around content moving on or off screen, like this auto-playing video.
Not only do you have more programatic control over your scroll view, but you also have more scroll positions to programmatically scroll to, like the top edge! There are all sorts of additional knobs for you to turn to really dial in on your perfect scrolling experience like turning off bouncing along a given axis, programmatically stoping scrolling, finely controlling your content alignment, and more. The new Swift 6 language mode enables compile-time data-race safety, and SwiftUI has improved its APIs to make it easier to adopt the new language mode in your apps.
Views in SwiftUI have always been evaluated on the main actor, and the view protocol is now marked with the main actor annotation to reflect that. That mean all types conforming to View are implicitly isolated to the main actor by default. So if you were explicitly marking your Views as main actor, you can now remove that annotation without any change in behavior. The new Swift 6 language mode is opt-in, so you can take advantage of it whenever you're ready. To learn more about compile time checking, be sure to watch "Migrate your app to Swift 6".
SwiftUI was designed not just for building brand new apps, but also building new features in existing apps written with UIKit and AppKit. Great interoperability with these frameworks is critical. We made significant improvements to the integration of gestures and animation. You can now take any built-in or custom UIGestureRecognizer and use it in your SwiftUI view hierarchy. This works even on SwiftUI views that aren't directly backed by UIKit.
And the interoperability improvements go the other way as well. UIKit and AppKit can now take advantage of the power of SwiftUI animations. SwiftUI defines a new animate function on UIView, and NSAnimationContext, allowing UIKit and AppKit changes to be animated using in-process SwiftUI animations.
Velocity is even automatically preserved for gesture-driven animations, just like in SwiftUI views. And UI and NSViewRepresentable context provide new API for bridging animations started in SwiftUI into UIKit and AppKit, ensuring animations run perfectly in sync, even across framework boundaries.
For more on using animations across frameworks, be sure to check out "Enhance your UI animations and transitions". Those foundational improvements look great. Now, all that’s left to is practice my karaoke skills, and then I can get together with Sommer and host the party. SwiftUI’s new tools for crafting experiences will help with both building a great practice app, and setting up the event to be a success.
New APIs for working with volumes, and immersive spaces, as well as new text effects will help bring our karaoke experience to life.
To practice my beautiful vocals, I’ve built a visionOS practice app, featuring a microphone in a Volume.
In visionOS 2, volumes can show a baseplate. This helps you get a sense of the bounds of the Volume, and guides you to the window controls, including the new resize handle. Our microphone already has a microphone stand base, so I like the look of it without the system provided baseplate. I'll use the .volumeBaseplateVisibility modifier to disable the system provided baseplate. Nice! I also made my microphone rotate to always face the singer using the new .onVolumeViewpointChange modifier. This is called any time I move to a new side of the Volume, letting me react to changes in how it’s being viewed.
I’ve got my microphone, now I need a place to put it, and there’s no better place to sing karaoke than a moody karaoke lounge.
I’ve already got a beautiful immersive space, and I can now control the allowed immersion levels.
By choosing an initial immersion of fifty percent, and a minimum of forty, I can help singers ease into the karaoke lounge.
To help set the vibe, I can now apply effects to the passthrough video around my progressive immersive space. I could use preferred-surroundings-effect to dim video passthrough or to really make our karaoke experience special, I can use colorMultiply for some cool mood lighting! Ooooh, that’s fantastic.
For more on improvements to volumes and immersive spaces, including new ways to attach ornaments and specify supported viewpoints be sure to check out “Dive deep into volumes and immersive spaces”.
The stage is set but before we start the party, we’ll need lyrics we can follow along with! You can now extend SwiftUI Text views with custom rendering effects and interaction behaviors. I can use this to build a word highlighting effect for our karaoke lyrics. My karaoke renderer creates a copy of the text behind the original drawing, which is blurred. and has a tint, making the text look like it's glowing purple! By applying this highlight effect to only certain words, and adding some final polish, I can make some truly incredible effects, like this karaoke word highlighting. To learn all about how to create these amazing text effects, be sure to check out "Create custom visual effects with SwiftUI." Alright Sommer, I think we’ve done all of our party prep. It really is coming together! These changes to SwiftUI really helped Sommer and I make the most of our apps, and we want you to make the most of yours too. If you have a sidebar-based iPad or tvOS app, leverage the new tab view APIs to allow for more flexibility, and for document based apps, enhance your new document launch experience. Add new windowing and input capabilities to your macOS and visionOS apps. Make the most of your live activity by fine-tuning your experience on watchOS. And take advantage of the new capabilities in volumes and immersive spaces on visionOS. It’s looking like a good year to be a SwiftUI developer! And karaoke singer. Alright Sam, are you ready? I was born ready Sommer. Hey Siri, Start the Party!
-
-
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() }
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.