Loading...
Loading...
Use when implementing iOS 26 SwiftUI features - covers Liquid Glass design system, performance improvements, @Animatable macro, 3D spatial layout, scene bridging, WebView/WebPage, AttributedString rich text editing, drag and drop enhancements, and visionOS integration for iOS 26+
npx skill4agent add charleswiltgen/axiom axiom-swiftui-26-refenabledBoundsaxiom-liquid-glassaxiom-liquid-glass-ref// No code changes required - recompile and get new design
NavigationSplitView {
List {
// Sidebar automatically gets glassy appearance on iPad/macOS
}
} detail: {
// Detail view
}
// Tab bars automatically compact on iPhone
TabView {
// Tabs get new appearance
}.toolbar {
ToolbarItemGroup(placement: .topBarTrailing) {
Button("Up") { }
Button("Down") { }
// Fixed spacer separates button groups
ToolbarSpacer(.fixed)
Button("Settings") { }
}
}ToolbarItemGroup.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel", systemImage: "xmark") {}
}
ToolbarItemGroup(placement: .primaryAction) {
Button("Draw", systemImage: "pencil") {}
Button("Erase", systemImage: "eraser") {}
}
ToolbarSpacer(.flexible)
ToolbarItem(placement: .confirmationAction) {
Button("Save", systemImage: "checkmark") {}
}
}ToolbarItemPlacementconfirmationActionglassProminentcancellationActionButton("Add Trip") {
addTrip()
}
.buttonStyle(.borderedProminent)
.tint(.blue)
// Liquid Glass toolbars support tinting for prominence.badge()ToolbarItem(placement: .confirmationAction) {
Button("Done", systemImage: "checkmark") { }
.badge(3) // Badge count on glass toolbar item
}sharedBackgroundVisibility(.hidden).toolbar {
ToolbarItem(placement: .topBarTrailing) {
// Avatar appears without glass background pill
Image(systemName: "person.crop.circle")
.sharedBackgroundVisibility(.hidden)
}
}.tint().toolbar {}struct DefaultToolbarItem {
init(kind: ToolbarDefaultItemKind, placement: ToolbarItemPlacement = .automatic)
}kindDefaultToolbarItemNavigationSplitView {
AllCalendarsView()
} detail: {
SelectedCalendarView()
.searchable(text: $query)
.toolbar {
ToolbarItem(placement: .bottomBar) {
CalendarPicker()
}
ToolbarItem(placement: .bottomBar) {
Invites()
}
DefaultToolbarItem(kind: .search, placement: .bottomBar)
ToolbarSpacer(placement: .bottomBar)
ToolbarItem(placement: .bottomBar) { NewEventButton() }
}
}DefaultToolbarItem.searchNavigationSplitView {
SidebarView()
.toolbar {
DefaultToolbarItem(kind: .search, placement: .bottomBar)
}
} content: {
ContentView()
} detail: {
DetailView()
}
.searchable(text: $text).searchable()NavigationSplitView.toolbar {
if #available(iOS 26.0, *) {
DefaultToolbarItem(kind: .search, placement: .bottomBar)
ToolbarSpacer(.flexible, placement: .bottomBar)
}
ToolbarItem(placement: .bottomBar) {
NewNoteButton()
}
}
.searchable(text: $searchText)struct MailboxList: View {
var body: some View {
List(mailboxes) { mailbox in
NavigationLink(mailbox.name, value: mailbox)
}
.toolbar { // ← Attached to this view, not NavigationStack
ToolbarItem(placement: .bottomBar) {
Button("Filter", systemImage: "line.3.horizontal.decrease") { }
}
ToolbarSpacer(.flexible, placement: .bottomBar)
ToolbarItem(placement: .bottomBar) {
Button("New Message", systemImage: "square.and.pencil") { }
}
}
}
}
struct MessageList: View {
let mailbox: Mailbox
var body: some View {
List(mailbox.messages) { message in
MessageRow(message: message)
}
.toolbar { // ← Different toolbar — iOS 26 morphs between them
ToolbarSpacer(.flexible, placement: .bottomBar)
ToolbarItem(placement: .bottomBar) {
Button("New Message", systemImage: "square.and.pencil") { }
}
}
}
}.toolbar {}toolbar(id:)ToolbarItem(id:)struct MailboxList: View {
var body: some View {
List(mailboxes) { mailbox in
NavigationLink(mailbox.name, value: mailbox)
}
.toolbar(id: "main") {
ToolbarItem(id: "filter", placement: .bottomBar) {
Button("Filter", systemImage: "line.3.horizontal.decrease") { }
}
ToolbarSpacer(.flexible, placement: .bottomBar)
ToolbarItem(id: "compose", placement: .bottomBar) {
Button("New Message", systemImage: "square.and.pencil") { }
}
}
}
}
struct MessageList: View {
let mailbox: Mailbox
var body: some View {
List(mailbox.messages) { message in
MessageRow(message: message)
}
.toolbar(id: "main") {
// "filter" absent — animates out during push
ToolbarSpacer(.flexible, placement: .bottomBar)
ToolbarItem(id: "compose", placement: .bottomBar) {
// Same ID as MailboxList — stays stable during morph
Button("New Message", systemImage: "square.and.pencil") { }
}
}
}
}struct MailApp: View {
var body: some View {
NavigationStack {
MailboxList()
.navigationDestination(for: Mailbox.self) { mailbox in
MessageList(mailbox: mailbox)
}
.navigationDestination(for: Message.self) { message in
MessageDetail(message: message)
}
}
}
}
// Each destination defines its own toolbar — NavigationStack morphs between them.fixed.toolbar {
ToolbarItem(placement: .bottomBar) {
Button("Archive", systemImage: "archivebox") { }
}
ToolbarSpacer(.flexible, placement: .bottomBar) // Push apart
ToolbarItem(placement: .bottomBar) {
Button("Compose", systemImage: "square.and.pencil") { }
}
}.navigationTitle("Inbox")
.navigationSubtitle("3 unread messages")toolbar(id:content:)TextEditor(text: $text)
.toolbar(id: "editingtools") {
ToolbarItem(id: "bold", placement: .secondaryAction) {
Toggle(isOn: $bold) { Image(systemName: "bold") }
}
ToolbarItem(id: "italic", placement: .secondaryAction) {
Toggle(isOn: $italic) { Image(systemName: "italic") }
}
}.secondaryActionToolbarItem(id: "advanced", placement: .secondaryAction, showsByDefault: false) {
// Hidden by default — user can add from customization editor
AdvancedFormattingControls()
}ToolbarCommands()@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
ToolbarCommands()
}
}
}CustomizableToolbarContentToolbarCustomizationBehaviorToolbarCustomizationOptionsContentView()
.toolbar(id: "main-toolbar") {
ToolbarItem(id: "tag") { TagButton() }
ToolbarItem(id: "share") { ShareButton() }
ToolbarSpacer(.fixed)
ToolbarItem(id: "more") { MoreButton() }
}ScrollView {
// When content scrolls under toolbar/navigation bar,
// blur effect automatically ensures bar content remains legible
ForEach(trips) { trip in
TripRow(trip: trip)
}
}
// No code required - automatic scroll edge blur.searchableisSearchingaxiom-swiftui-search-refNavigationSplitView {
List { }
.searchable(text: $searchText)
}
// Placement on NavigationSplitView automatically:
// - Bottom-aligned on iPhone (more ergonomic)
// - Top trailing corner on iPadplacement: .sidebarNavigationSplitView {
List { }
.searchable(text: $searchText, placement: .sidebar)
// Search field embedded in sidebar instead of floating glass container
}searchToolbarBehavior(.minimize).searchableTabView {
BrowseView()
.tabItem { Label("Browse", systemImage: "square.grid.2x2") }
Tab(role: .search) {
SearchView()
}
}
.searchable(text: $searchText)Tab(role: .search)struct ToTopButton: View {
var body: some View {
Button("To Top", systemImage: "chevron.up") {
scrollToTop()
}
.padding()
.glassEffect() // Reflects surrounding content
}
func scrollToTop() { }
}.interactiveGlassEffectContainerGlassEffectContainer {
HStack(spacing: 12) {
ForEach(badges) { badge in
BadgeView(badge: badge)
.glassEffect()
}
}
}glassEffectID@Namespace private var badgeNamespace
var body: some View {
if isExpanded {
// Expanded badge stack
GlassEffectContainer {
VStack {
ForEach(badges) { badge in
BadgeView(badge: badge)
.glassEffect()
.glassEffectID(badge.id, in: badgeNamespace)
}
}
}
} else {
// Collapsed into toolbar button
Button("Badges", systemImage: "star.fill") {
isExpanded.toggle()
}
.glassEffect()
.glassEffectID("collapsed", in: badgeNamespace)
}
}
// Tapping the button expands badges with fluid glass morphing;
// tapping again re-absorbs them into the buttonpresentationBackground// ❌ Custom background interferes with Liquid Glass sheet material
.sheet(isPresented: $showDetail) {
DetailView()
.presentationBackground(.thinMaterial) // Remove this
}
// ✅ System applies Liquid Glass automatically
.sheet(isPresented: $showDetail) {
DetailView()
}navigationZoomTransition.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Details", systemImage: "info.circle") {
showDetails = true
}
.matchedTransitionSource(id: "details", in: namespace)
}
}
.sheet(isPresented: $showDetails) {
DetailsSheet()
.navigationTransition(.zoom(sourceID: "details", in: namespace))
}// Capsule is now the default bordered shape on iOS
Button("Action") { }
.buttonStyle(.bordered) // Capsule shape on iOS 26
// Override with buttonBorderShape if needed
Button("Action") { }
.buttonStyle(.bordered)
.buttonBorderShape(.roundedRectangle) // Force rounded rectangleButton("Get Started") { startOnboarding() }
.buttonStyle(.borderedProminent)
.controlSize(.extraLarge) // New extra-large sizecontrolSize// Preserve density in complex inspectors
Form {
// controls here
}
.controlSize(.small) // Maintains pre-iOS 26 densityLabelMenu("Actions") {
Button("Copy", systemImage: "doc.on.doc") { copy() }
Button("Paste", systemImage: "doc.on.clipboard") { paste() }
Button("Delete", systemImage: "trash", role: .destructive) { delete() }
}
// Same result on iOS and macOS — icons on leading edge// Automatic concentricity with container
Button("Confirm") { confirm() }
.clipShape(.rect(cornerRadius: 12, style: .containerConcentric))
// Shape automatically matches container across displays and window shapesSlider| Type | Purpose |
|---|---|
| Individual tick at a specific value with optional label |
| Iterate over collection to create multiple ticks |
| Result builder for composing tick content |
| Internal type for multiple inline ticks |
| Protocol that all tick types conform to |
struct SpeedSlider: View {
@State private var speed: Double = 0.5
var body: some View {
Slider(value: $speed) {
Text("Speed")
} ticks: {
SliderTick(0.2)
SliderTick(0.5)
SliderTick(0.8)
}
}
}Slider(value: $value, in: 0...10) {
Text("Rating")
} ticks: {
SliderTick(0) { Text("Min") }
SliderTick(5) { Text("Mid") }
SliderTick(10) { Text("Max") }
}struct TemperatureSlider: View {
@State private var temp: Float = 70
var body: some View {
let stops: [Float] = stride(from: 60, through: 80, by: 5).map { Float($0) }
Slider(value: $temp, in: 60...80) {
Text("Temperature")
} ticks: {
SliderTickContentForEach(stops, id: \.self) { value in
SliderTick(value) {
Text("\(Int(value))°")
.font(.caption2)
}
}
}
}
}SliderTickContentForEachData.ElementSliderTick<V>struct Chapter {
let time: Double
let name: String
let id: UUID
}
// ERROR: Data.Element (Chapter) doesn't match SliderTick value type (Double)
SliderTickContentForEach(chapters, id: \.id) { chapter in
SliderTick(chapter.time) { Text(chapter.name) }
}struct ChapterSlider: View {
@Binding var currentTime: Double
let chapters: [Chapter]
let duration: Double
var body: some View {
Slider(value: $currentTime, in: 0...duration) {
Text("Time")
} ticks: {
// Iterate over Double values, not Chapter structs
SliderTickContentForEach(chapters.map(\.time), id: \.self) { time in
SliderTick(time) {
// Look up chapter name for label
if let chapter = chapters.first(where: { $0.time == time }) {
Text(chapter.name)
.font(.caption2)
}
}
}
}
}
}BinaryFloatingPointSlider(
value: $rating,
in: 0...100,
neutralValue: 50, // Starting point / center value
enabledBounds: 20...80, // Restrict selectable range
label: { Text("Rating") },
currentValueLabel: { Text("\(Int(rating))") },
minimumValueLabel: { Text("0") },
maximumValueLabel: { Text("100") },
ticks: {
SliderTick(20) { Text("Min") }
SliderTick(50) { Text("Neutral") }
SliderTick(80) { Text("Max") }
},
onEditingChanged: { editing in
print(editing ? "Started" : "Ended")
}
)| Parameter | Type | Purpose |
|---|---|---|
| | Current slider value |
| | Full value range (default: |
| | Increment between valid values |
| | Starting/center point |
| | Restrict which values are selectable |
| | Custom tick marks |
| | Shows current value |
| | Called when editing starts/ends |
Slider(
value: $volume,
in: 0...10,
step: 2,
label: { Text("Volume") },
tick: { value in
// Called for each step value (0, 2, 4, 6, 8, 10)
SliderTick(value) {
Text("\(Int(value))")
}
}
)struct MediaControlView: View {
@State private var progress: CGFloat = 0.5
var body: some View {
Slider(value: $progress)
.sliderThumbVisibility(.hidden)
.padding(.horizontal, 16)
}
}.automatic.visible.hiddenstruct MediaPlayerControls: View {
@State private var currentTime: Double = 0
let duration: Double = 300 // 5 minutes
let chapters: [Chapter] = [
Chapter(time: 0, name: "Intro", id: UUID()),
Chapter(time: 60, name: "Verse 1", id: UUID()),
Chapter(time: 120, name: "Chorus", id: UUID()),
Chapter(time: 180, name: "Verse 2", id: UUID()),
Chapter(time: 240, name: "Outro", id: UUID())
]
var body: some View {
VStack {
// Time display
HStack {
Text(formatTime(currentTime))
Spacer()
Text(formatTime(duration))
}
.font(.caption)
// Slider with chapter ticks
Slider(
value: $currentTime,
in: 0...duration,
label: { Text("Playback") },
currentValueLabel: {
if let chapter = currentChapter {
Text(chapter.name)
.font(.caption)
}
},
ticks: {
SliderTickContentForEach(chapters.map(\.time), id: \.self) { time in
SliderTick(time)
}
}
)
.sliderThumbVisibility(.hidden)
}
.padding()
}
var currentChapter: Chapter? {
chapters.last { $0.time <= currentTime }
}
func formatTime(_ seconds: Double) -> String {
let mins = Int(seconds) / 60
let secs = Int(seconds) % 60
return String(format: "%d:%02d", mins, secs)
}
}struct ContentView: View {
var body: some View {
NavigationStack {
List {
ForEach(1...20, id: \.self) { index in
Text("\(index). Item")
}
}
.safeAreaBar(edge: .bottom) {
Text("Bottom Action Bar")
.padding(.vertical, 15)
}
.scrollEdgeEffectStyle(.soft, for: .bottom)
// Alternative: .scrollEdgeEffectStyle(.hard, for: .bottom)
}
}
}safeAreaInset.soft.hardscrollEdgeEffectStylestruct LinkView: View {
@Environment(\.openURL) var openURL
var body: some View {
let website = URL(string: "https://example.com")!
VStack {
// Old style - opens in Safari
Link(destination: website) {
Text("Open in Safari")
}
// New style - opens in-app (iOS 26+)
Button("Open In-App") {
openURL(website, prefersInApp: true)
}
.buttonStyle(.borderedProminent)
}
}
}LinkopenURL(url, prefersInApp: true)struct ModalView: View {
@State private var showSheet = false
var body: some View {
Button("Show Sheet") {
showSheet.toggle()
}
.sheet(isPresented: $showSheet) {
NavigationStack {
VStack {}
.navigationTitle("Info")
.toolbar {
ToolbarSpacer(.flexible, placement: .topBarTrailing)
ToolbarItem(placement: .topBarTrailing) {
Button(role: .close) {
showSheet = false
}
}
}
}
.presentationDetents([.medium])
}
}
}Button(role: .close)Button(role: .confirm)struct GlassButtonExample: View {
var body: some View {
ZStack {
Image(.background)
.resizable()
.aspectRatio(contentMode: .fill)
.ignoresSafeArea()
VStack(spacing: 16) {
Button("Clear Glass") {}
.buttonStyle(GlassButtonStyle(.clear))
Button("Regular Glass") {}
.buttonStyle(GlassButtonStyle(.glass))
Button("Tinted Glass") {}
.buttonStyle(GlassButtonStyle(.tint))
.tint(.blue)
}
.fontWeight(.bold)
.foregroundStyle(.white)
.buttonSizing(.flexible)
.font(.title)
.padding()
}
}
}.clear.glass.tint.tint()struct ButtonLayoutExample: View {
var body: some View {
VStack(spacing: 16) {
Button("Fit Content") {}
.buttonSizing(.fit)
// Button shrinks to label size
Button("Stretch Full Width") {}
.buttonSizing(.stretch)
// Button expands to fill available space
Button("Flexible") {}
.buttonSizing(.flexible)
// Balanced between fit and stretch
}
.padding()
}
}.fit.stretch.flexible.searchable.searchToolbarBehavioraxiom-swiftui-search-refstruct SearchView: View {
@State private var searchText = ""
var body: some View {
NavigationStack {
List {
Text("User 1")
Text("User 2")
Text("User 3")
}
.navigationTitle("Search Users")
.searchable(text: $searchText)
.searchToolbarBehavior(.minimize)
.toolbar {
ToolbarSpacer(.flexible, placement: .bottomBar)
DefaultToolbarItem(kind: .search, placement: .bottomBar)
}
}
}
}SearchToolbarBehavior.minimize.automaticextension View {
@ViewBuilder func minimizedSearch() -> some View {
if #available(iOS 26.0, *) {
self.searchToolbarBehavior(.minimize)
} else { self }
}
}
// Usage
.searchable(text: $searchText)
.minimizedSearch().searchable(text: $searchText)
.searchPresentationToolbarBehavior(.avoidHidingContent).avoidHidingContentsearchToolbarBehavior.commands {
TextEditingCommands() // Same API as macOS menu bar
CommandGroup(after: .newItem) {
Button("Add Note") {
addNote()
}
.keyboardShortcut("n", modifiers: [.command, .shift])
}
}
// Creates menu bar on iPad when people swipe down// MIGRATION REQUIRED:
// Remove deprecated property list key in iPadOS 26:
// UIRequiresFullscreen (entire key deprecated, all values)
// For split view navigation, system automatically shows/hides columns
// based on available space during resize
NavigationSplitView {
Sidebar()
} detail: {
Detail()
}
// Adapts to resizing automatically.windowResizeAnchor(.topLeading) // Tailor where animation originates
// SwiftUI now synchronizes animation between content view size changes
// and window resizing - great for preserving continuity when switching tabsList(trips) { trip in // 100k+ items
TripRow(trip: trip)
}
// Loads 6x faster, updates 16x faster on macOS (iOS 26+)ScrollView(.horizontal) {
LazyHStack {
ForEach(photoSets) { photoSet in
ScrollView(.vertical) {
LazyVStack {
ForEach(photoSet.photos) { photo in
PhotoView(photo: photo)
}
}
}
}
}
}
// Nested scrollviews now properly delay loading with lazy stacks
// Great for building photo carousels@Observable
class TripStore {
var trips: [Trip] = []
func loadTrips() async {
trips = await TripService.fetchTrips()
// Swift 6 verifies data race safety at compile time
}
}animatableDatastruct HikingRouteShape: Shape {
var startPoint: CGPoint
var endPoint: CGPoint
var elevation: Double
var drawingDirection: Bool // Don't want to animate this
// Tedious manual animatableData declaration
var animatableData: AnimatablePair<CGPoint.AnimatableData,
AnimatablePair<Double, CGPoint.AnimatableData>> {
get {
AnimatablePair(startPoint.animatableData,
AnimatablePair(elevation, endPoint.animatableData))
}
set {
startPoint.animatableData = newValue.first
elevation = newValue.second.first
endPoint.animatableData = newValue.second.second
}
}
}@Animatable
struct HikingRouteShape: Shape {
var startPoint: CGPoint
var endPoint: CGPoint
var elevation: Double
@AnimatableIgnored
var drawingDirection: Bool // Excluded from animation
// animatableData automatically synthesized!
}animatableData@AnimatableIgnoredstruct SunPositionView: View {
@State private var timeOfDay: Double = 12.0
var body: some View {
HikingRouteView()
.overlay(alignment: sunAlignment) {
SunView()
.spatialOverlay(alignment: sunAlignment)
}
}
var sunAlignment: Alignment3D {
// Align sun in 3D space based on time of day
Alignment3D(
horizontal: .center,
vertical: .top,
depth: .back
)
}
}Model3D(named: "WaterBottle")
.manipulable() // People can pick up and move the object@Environment(\.surfaceSnappingInfo) var snappingInfo: SurfaceSnappingInfo
var body: some View {
VStackLayout().depthAlignment(.center) {
Model3D(named: "waterBottle")
.manipulable()
Pedestal()
.opacity(snappingInfo.classification == .table ? 1.0 : 0.0)
}
}MenuBarExtraImmersiveSpaceRemoteImmersiveSpaceAssistiveAccess.windowStyle().immersiveEnvironmentBehavior()// In your macOS app
@main
struct MyMacApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
RemoteImmersiveSpace(id: "stereoView") {
// Render stereo content on Apple Vision Pro
// Uses CompositorServices
}
}
}@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
AssistiveAccessScene {
SimplifiedUI() // UI shown when iPhone is in AssistiveAccess mode
}
}
}// Show SwiftUI view in AppKit sheet
let hostingController = NSHostingController(rootView: SwiftUISettingsView())
presentAsSheet(hostingController)
// Great for incremental SwiftUI adoption// Bridge AppKit gestures to SwiftUI
struct AppKitPanGesture: NSGestureRecognizerRepresentable {
func makeNSGestureRecognizer(context: Context) -> NSPanGestureRecognizer {
NSPanGestureRecognizer()
}
func updateNSGestureRecognizer(_ recognizer: NSPanGestureRecognizer, context: Context) {
// Update configuration
}
}@Observable
class RealityEntity {
var position: SIMD3<Float>
var rotation: simd_quatf
}
struct MyView: View {
@State private var entity = RealityEntity()
var body: some View {
// SwiftUI views automatically observe changes
Text("Position: \(entity.position.x)")
}
}// Present SwiftUI popovers from RealityKit entities
let popover = Entity()
mapEntity.addChild(popover)
popover.components[PresentationComponent.self] = PresentationComponent(
isPresented: $popoverPresented,
configuration: .popover(arrowEdge: .bottom),
content: DetailsView()
)ViewAttachmentComponentGestureComponentimport WebKit
struct ArticleView: View {
let articleURL: URL
var body: some View {
WebView(url: articleURL)
}
}import WebKit
struct InAppBrowser: View {
@State private var page = WebPage()
var body: some View {
VStack {
Text(page.title ?? "Loading...")
WebView(page)
.ignoresSafeArea()
.onAppear {
page.load(URLRequest(url: articleURL))
}
HStack {
Button("Back") { page.goBack() }
.disabled(!page.canGoBack)
Button("Forward") { page.goForward() }
.disabled(!page.canGoForward)
}
}
}
}goBack()goForward()titleurlcanGoBackcanGoForwardTextEditorAttributedStringstruct CommentView: View {
@State private var comment = AttributedString("Enter your comment")
var body: some View {
TextEditor(text: $comment)
// Built-in text formatting controls included
// Users can apply bold, italic, underline, etc.
}
}AttributedStringstruct PhotoGrid: View {
@State private var selectedPhotos: [Photo.ID] = []
var body: some View {
ScrollView {
LazyVGrid(columns: gridColumns) {
ForEach(model.photos) { photo in
view(photo: photo)
.draggable(containerItemID: photo.id)
}
}
}
.dragContainer(for: Photo.self, selection: selectedPhotos) { draggedIDs in
photos(ids: draggedIDs)
}
}
}.draggable(containerItemID:containerNamespace:)nil.dragContainer(for:selection:).dragConfiguration(DragConfiguration(allowMove: false, allowDelete: true)).onDragSessionUpdated { session in
let ids = session.draggedItemIDs(for: Photo.ID.self)
if session.phase == .ended(.delete) {
trash(ids)
deletePhotos(ids)
}
}.dragPreviewsFormation(.stack) // Items stack nicely on top of one another
// Other formations:
// - .default
// - .grid
// - .stackstruct PhotoLibrary: View {
@State private var selectedPhotos: [Photo.ID] = []
var body: some View {
ScrollView {
LazyVGrid(columns: gridColumns) {
ForEach(model.photos) { photo in
view(photo: photo)
.draggable(containerItemID: photo.id)
}
}
}
.dragContainer(for: Photo.self, selection: selectedPhotos) { draggedIDs in
photos(ids: draggedIDs)
}
.dragConfiguration(DragConfiguration(allowMove: false, allowDelete: true))
.dragPreviewsFormation(.stack)
.onDragSessionUpdated { session in
let ids = session.draggedItemIDs(for: Photo.ID.self)
if session.phase == .ended(.delete) {
trash(ids)
deletePhotos(ids)
}
}
}
}Chart3Dimport Charts
struct HikePlotView: View {
var body: some View {
Chart3D {
SurfacePlot(x: "x", y: "y", z: "z") { x, y in
sin(x) * cos(y)
}
.foregroundStyle(Gradient(colors: [.orange, .pink]))
}
.chartXScale(domain: -3...3)
.chartYScale(domain: -3...3)
.chartZScale(domain: -3...3)
}
}Chart3DSurfacePlot.chartZScale().chartZAxis()LineMark3Dstruct FavoriteLocationControl: ControlWidget {
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(kind: "FavoriteLocation") {
ControlWidgetButton(action: MarkFavoriteIntent()) {
Label("Mark Favorite", systemImage: "star")
}
}
}
}
// Access from watch face or Shortcutsstruct CountdownWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "Countdown") { entry in
CountdownView(entry: entry)
}
}
}
struct PhotoCountdownView: View {
@Environment(\.levelOfDetail) var levelOfDetail: LevelOfDetail
var body: some View {
switch levelOfDetail {
case .default:
RecentPhotosView() // Full detail when close
case .simplified:
CountdownView() // Simplified when further away
default:
CountdownView()
}
}
}<key>UIRequiresFullscreen</key>
<!-- Entire property list key is deprecated (all values) -->presentationBackground.fixed.glassEffect()glassEffectIDGlassEffectContainersharedBackgroundVisibility(.hidden)navigationZoomTransitionTab(role: .search).searchToolbarBehavior(.minimize).controlSize(.extraLarge).containerConcentric.commands.windowResizeAnchor().dragContainerSliderTickSliderTickContentForEach.sliderThumbVisibility().safeAreaBar().scrollEdgeEffectStyle()openURL(url, prefersInApp: true)Button(role: .close)GlassButtonStyle.buttonSizing().toolbar {}toolbar(id:)toolbar(id:)CustomizableToolbarContent.tabBarMinimizeBehavior(.onScrollDown).tabViewBottomAccessory(isEnabled:content:)toolbar(id:)presentationBackgroundGlassEffectContainer.safeAreaPadding().safeAreaPadding().padding().padding().safeAreaPadding()axiom-swiftui-layout-ref.safeAreaPadding().padding()axiom-liquid-glass-refAttributedStringTextEditorStringAlignment3D.manipulable()// ✅ CORRECT: searchable on NavigationSplitView
NavigationSplitView {
List { }
.searchable(text: $query)
}
// ❌ WRONG: searchable on List directly in non-navigation context
List { }
.searchable(text: $query)// Ensure all properties are either:
// 1. VectorArithmetic conforming types (Double, CGFloat, CGPoint, etc.)
// 2. Marked with @AnimatableIgnored
@Animatable
struct MyShape: Shape {
var radius: Double // ✅ VectorArithmetic
var position: CGPoint // ✅ VectorArithmetic
@AnimatableIgnored
var fillColor: Color // ✅ Ignored (Color is not VectorArithmetic)
}// ✅ CORRECT: Binding to AttributedString
@State private var text = AttributedString("Hello")
TextEditor(text: $text)
// ❌ WRONG: Binding to String
@State private var text = "Hello"
TextEditor(text: $text) // Plain String loses formatting// Must enable delete in drag configuration
.dragConfiguration(DragConfiguration(allowMove: false, allowDelete: true))
// And observe the delete event
.onDragSessionUpdated { session in
if session.phase == .ended(.delete) {
deleteItems()
}
}[Chapter]// ERROR: Cannot convert value of type 'Chapter' to expected argument type
SliderTickContentForEach(chapters, id: \.id) { chapter in
SliderTick(chapter.time) { ... }
}SliderTickContentForEachData.ElementSliderTick<V>// ✅ CORRECT: Iterate over Double values
SliderTickContentForEach(chapters.map(\.time), id: \.self) { time in
SliderTick(time) {
if let chapter = chapters.first(where: { $0.time == time }) {
Text(chapter.name)
}
}
}// ❌ WRONG: Toolbar on NavigationStack — nothing to morph between
NavigationStack {
ContentView()
}
.toolbar {
ToolbarItem { Button("Action") { } }
}
// ✅ CORRECT: Toolbar on each destination view — iOS 26 morphs between them
NavigationStack {
ListView()
.toolbar {
ToolbarItem { Button("Filter") { } }
}
.navigationDestination(for: Item.self) { item in
DetailView(item: item)
.toolbar {
ToolbarItem { Button("Edit") { } }
}
}
}.toolbar {}