WWDC Quick Look đź’“ By SwiftGGTeam
Build a SwiftUI app with the new design

Build a SwiftUI app with the new design

Watch original video

Highlight

Franck uses the Landmarks sample project to demonstrate rebuilding a SwiftUI app under the Xcode 26 SDK, covering changes across five areas: App structure, Toolbars, Search, Controls, and custom Liquid Glass.

Core Content

Each year a new SDK drops, developers fear the same thing: “it looks better, but I have to rewrite all my code.” This time iOS 26 / macOS Tahoe introduces Liquid Glass, and the first reaction is similar: the sidebar is glass, the tab bar floats, buttons became capsule-shaped—do I need to rewrite all my existing toolbars, sheets, and buttons?

Franck’s answer: recompile with Xcode 26 first, see what you get for free, then decide whether to write code. He walks through a real Landmarks sample app—the hero image is cropped by the sidebar, five toolbar buttons have no visual hierarchy, the search bar placement differs between iPhone and iPad. Under the old APIs, these problems required hand-written adaptation code. The new SDK turns them into one or two modifiers: backgroundExtensionEffect extends the image beyond the safe area automatically, ToolbarSpacer groups toolbar items by semantics, searchable on NavigationSplitView adapts to the iPhone bottom and iPad top trailing automatically. The demo logic is clear: eat the automatic improvements first, write new APIs only for the semantics you need to express.

Detail

App structure: sidebar defaults to Liquid Glass, floating above content. If the sidebar crops the hero image, use backgroundExtensionEffect to extend the image reflection outside the safe area (03:59).

// Extend background images

struct LandmarkDetailView: View {
    let landmark: Landmark

    var body: some View {
        ScrollView {
            VStack {
                Image(landmark.backgroundImageName)
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .backgroundExtensionEffect()
            }
        }
    }
}

Key points:

  • .backgroundExtensionEffect() mirrors and blurs the image outside the safe area, while the original content remains visible inside.
  • No need to manually calculate sidebar width or re-crop images—the reflection area expands automatically when the sidebar hides.

TabView’s tab bar becomes floating on iPhone, can minimize on scroll, and supports an accessory above it (like Music’s playback bar) (05:39).

// Tab bar accessory

TabView {
    // tabs
}
.tabBarMinimizeBehavior(.onScrollDown)
.tabViewBottomAccessory {
    MusicPlaybackView()
}

struct MusicPlaybackView: View {
    @Environment(\.tabViewBottomAccessoryPlacement)
    var placement

    var body: some View {
        if placement == .inline {
            // compact layout
        } else {
            // full layout
        }
    }
}

Key points:

  • tabBarMinimizeBehavior(.onScrollDown) minimizes the tab bar on scroll down, restores on scroll up.
  • tabViewBottomAccessory places a persistent view above the tab bar, using the space freed when minimized.
  • Use the environment value tabViewBottomAccessoryPlacement to distinguish between .inline (embedded when minimized) and expanded layouts.

Toolbars: items group automatically, use ToolbarSpacer to explicitly control semantic boundaries (08:26).

// Visually separate toolbar items

struct LandmarkDetailView: View {
    var body: some View {
        ScrollView {
            ScrollContent()
        }
        .toolbar {
            ToolbarItem { ShareLink() }
            ToolbarSpacer(.fixed)
            ToolbarItem { FavoriteButton() }
            ToolbarItem { CollectionsButton() }
            ToolbarSpacer(.fixed)
            ToolbarItem { InspectorToggle() }
        }
    }
}

Key points:

  • Adjacent ToolbarItems are merged into the same glass background group by default.
  • ToolbarSpacer(.fixed) forces a background group break, keeping favorite/collections as a separate group, with share and inspector each independent.
  • Use ToolbarSpacer(.flexible, placement: .bottomBar) for leading/trailing stretch (like Mail’s filter and compose at opposite ends of bottomBar).
  • To opt a single item out of the background group (like an avatar), use .sharedBackgroundVisibility(.hidden); to add a red dot, use .badge(count).

Search: put searchable on NavigationSplitView, the system places it at the bottom on iPhone and top trailing on iPad/Mac (11:44).

// Search in the top-trailing position

struct TopTrailingSearch: View {
    @State private var searchText = ""

    var body: some View {
        NavigationSplitView {
            SidebarContent()
        } detail: {
            DetailContent()
        }
        .searchable(text: $searchText)
    }
}

Key points:

  • searchable on NavigationSplitView means search applies to the entire view, not a single column.
  • iPhone automatically moves the search bar to the bottom for thumb reach; iPad/Mac automatically places it at toolbar trailing.
  • Multi-tab apps wanting a dedicated search page: Tab(role: .search) { ... } + searchable on TabView; when users switch to that tab, the search bar replaces the tab bar.
  • To force minimization to a toolbar button, use .searchToolbarBehavior(.minimize).

Controls: buttons default to capsule shape (small size on macOS remains rounded rectangle); new .glass / .glassProminent styles (15:33).

// Prominent glass button
Button("Get Started") { }
    .buttonStyle(.glassProminent)

// Standard glass button
Button("Learn More") { }
    .buttonStyle(.glass)

Key points:

  • .glassProminent is for calls-to-action (CTA), with automatic tint.
  • .glass is a standard glass button, background adapts to the content below.
  • Slider adds tick marks (auto-generated from step) and neutralValue (specifies offset from zero, for two-way values like EQ).
  • Use .rect(corner: .containerConcentric) for concentric corners, avoiding manual corner radius calculation.

Custom Liquid Glass: add .glassEffect() to any view, use GlassEffectContainer to combine multiple elements for morphing (19:51).

// GlassEffectContainer

@Namespace var namespace

GlassEffectContainer {
    VStack {
        if isExpanded {
            VStack(spacing: 16) {
                ForEach(badges) { badge in
                    BadgeLabel(badge: badge)
                        .glassEffect()
                        .glassEffectID(badge.id, in: namespace)
                }
            }
        }

        BadgeToggle()
            .buttonStyle(.glass)
            .glassEffectID("badgeToggle", in: namespace)
    }
}

Key points:

  • GlassEffectContainer lets multiple glass elements share a sampling region, avoiding visual fragmentation from independent sampling.
  • glassEffectID with the same @Namespace creates fluid morph transitions between elements during expand/collapse.
  • Individual elements can use .glassEffect(.regular.tint(.green)) for color, .glassEffect(.regular.interactive()) for press scaling and highlights.

Key Takeaways

  • What to do: recompile your project with Xcode 26 first, note which UI automatically improves and what breaks.

    • Why it’s worth it: many new effects (toolbar grouping, sheet embedded glass, scroll edge effect) come with zero code—this pass can save half the migration work.
    • How to start: switch to Xcode 26 on a new branch, build & run each major page, put visual comparison screenshots in a review doc, then decide which APIs need changes based on the issue list.
  • What to do: clean up hacks added in older versions to fight toolbar/sheet defaults (custom backgrounds, translucent overlays, presentationBackground).

    • Why it’s worth it: the new design’s automatic scroll edge effect and Liquid Glass material will clash with these old hacks—the more you keep, the worse it looks.
    • How to start: search for presentationBackground, .background(.regularMaterial), custom Color.black.opacity in your project, evaluate each for deletion to see the default effect.
  • What to do: regroup toolbar items by semantics, express explicitly with ToolbarSpacer(.fixed).

    • Why it’s worth it: users in the new design judge which buttons belong together by glass background continuity (like “favorite + add to collection” as a group, share and inspector independent). Ambiguous toolbars cause mis-taps.
    • How to start: list all toolbar buttons per page, group “related actions” in brackets; insert ToolbarSpacer(.fixed) between each two groups; add .sharedBackgroundVisibility(.hidden) to items outside groups.
  • What to do: move searchable up to the NavigationSplitView level, delete dual layouts written for iPhone/iPad.

    • Why it’s worth it: the system automatically places the search bar at the bottom on iPhone and toolbar trailing on iPad/Mac—one codebase works for both.
    • How to start: move existing searchable from the detail column to the outer NavigationSplitView; if search isn’t core, add .searchToolbarBehavior(.minimize) for auto-collapse.
  • What to do: try glassEffect + GlassEffectContainer on custom controls, see if you can replace hand-written card styles.

    • Why it’s worth it: custom controls wrapped in glass match system components visually, and GlassEffectContainer provides fluid morphing hard to achieve with hand-written animations.
    • How to start: pick a custom component with expand/collapse interaction (like floating toolbar, emoji panel) as experiment, add glassEffectID to each sub-element, wrap the outer layer in GlassEffectContainer, compare old and new animation effects.

Comments

GitHub Issues · utterances