Full-Screen Button

Tappable card with Apple's native zoom transition (App Store / Photos style) — the card geometry-matches into a true full-screen view via iOS 18 `.matchedTransitionSource` and `.navigationTransition(.zoom)`

Free
Free with a ShipSwift account.

Copies a short instruction with your private download link. Paste it into your agent: it downloads the whole skill (the source code, the setup steps and the mistakes we already made for you) and builds the screen.

Works with

+70

What it does

Tappable card that expands to fill the device display using Apple's native zoom transition — the same continuous, geometry-matched effect Apple uses for App Store / Photos / Music album-to-detail navigations. The compact card appears to spring into the entire screen rather than sliding up as a generic modal, and because the expanded view is pushed onto the host NavigationStack, it covers the screen edge-to-edge regardless of the surrounding ScrollView / VStack / nested layout.

Requires:

  • iOS 18+ (uses .matchedTransitionSource(id:in:) and .navigationTransition(.zoom(sourceID:in:)))
  • An enclosing NavigationStack — the component pushes its expanded view onto the host stack
  • An AccentColor color set in your asset catalog — the footer uses Xcode's generated .accent asset symbol (Xcode's default app template already includes one)

iOS only. File is named SWFullScreenButton+iOS.swift; in Xcode → Build Phases → Compile Sources set the platform filter for this file to iOS.

What you get

  • ✓182 lines of source code, ready to paste
  • ✓Source Code
  • ✓Usage
  • ✓Parameters
  • ✓Notes
animationfullscreenbuttonzoommatchedTransitionSourcenavigationTransitionSwiftUIiOS18

Usage

// Must be hosted inside a NavigationStack
NavigationStack {
    SWFullScreenButton()
}

// Custom copy
NavigationStack {
    SWFullScreenButton(
        title: "SmileMax",
        subtitle: "Daily smile analytics",
        footer: "Open"
    )
}

// Custom palette and shape
NavigationStack {
    SWFullScreenButton(
        gradientColors: [.purple, .pink],
        cornerRadius: 24
    )
}

Parameters

ParameterTypeDefaultDescription
titleString"ShipSwift"Top headline shown in white
subtitleString"Fullstack AI toolkit"Single-line tagline under the title
footerString"FullScreenCard"Bottom accent label rendered with the accent color
compactSizeCGSizeCGSize(width: 300, height: 300)Frame size in the collapsed state
gradientColors[Color][.brown, .white]Background gradient stops, applied top to bottom
cornerRadiusCGFloat30Card corner radius applied in the compact state

Notes

  • Why the zoom transition? An earlier version of this component animated its own .frame from compactSize up to UIScreen bounds with withAnimation(.bouncy). The bounce was the entire point of the component — but inside any NavigationStack or ScrollView, SwiftUI's layout system clamped the frame to the parent's content area, so the card grew toward the toolbar / scroll bounds and never reached the physical screen edges. A second iteration switched to .fullScreenCover, which fixed the "fills the screen" problem but threw away the original spring-into-fullscreen feel — .fullScreenCover's generic slide-up modal is something any vanilla SwiftUI app gets for free, so the component became redundant. The iOS 18 zoom transition gives back the best of both: a continuous, geometry-matched spring from the compact card into a real edge-to-edge view, with no manual screen-size measurement and no container clamping.
  • Dismiss gestures. The expanded view supports two dismiss paths: (1) tap anywhere on the card — the destination view holds @Environment(\.dismiss) and calls it on onTapGesture, which pops the NavigationStack and triggers the reverse zoom animation; (2) edge-swipe-back — the system-provided gesture for any pushed view, also reverse-zooms. The tap path matches the original "tap to toggle" intent of the component; the edge-swipe path is iOS-native muscle memory.
  • Why NavigationStack is required. The zoom transition is implemented as a NavigationLink push with a .navigationTransition(.zoom(...)) modifier on the destination. Without a host NavigationStack, the NavigationLink silently renders as a plain button and the transition never fires. The hosting requirement is what lets the zoom escape any ScrollView / nested VStack — the push goes through the stack, not through the layout tree.
  • iOS 18+ only. Both .matchedTransitionSource(id:in:) and .navigationTransition(.zoom(sourceID:in:)) were introduced in iOS 18, ShipSwift's minimum, so no @available annotation is needed. The file is named SWFullScreenButton+iOS.swift so Xcode's per-file platform filter can exclude it from macOS targets — do not wrap the body in #if os(iOS) as a substitute (per the ShipSwift convention, platform filtering is a build setting).
  • Tap target. The whole compact card is the NavigationLink label; .buttonStyle(.plain) removes the default tint so the gradient renders cleanly.
  • Shadow flicker on reverse zoom — the real fix. The iOS 18 zoom transition snapshots the source view's bounds raster. A .shadow(_:) is drawn outside those bounds, so the snapshot never carries it — regardless of whether the shadow is written as a trailing modifier (.shadow(radius: 30)) or embedded inside a .background { RoundedRectangle().shadow(...) }. The visible symptom: during the reverse zoom the shadow is absent, and one frame after the system hands off from the destination snapshot back to the live source view, the shadow "pops in." We solve this with an animated radius: a @State private var shadowRadius: CGFloat = 30 drives the background's shadow, the destination's .onAppear sets it to 0, and .onDisappear runs withAnimation(.easeOut(duration: 0.25)) { shadowRadius = 30 }. Reverse zoom completes with shadowRadius == 0, then the shadow fades back in smoothly — masking the snapshot hand-off frame. This is the only reliable fix on iOS 18; .compositingGroup(), .drawingGroup(), and reordering the modifiers do not extend the snapshot bounds.

More in Effects and animation

Metal shaders: fields of dots that roll like waves and flow into living patterns, all drawn on the GPU.
Metal dot fields
Free
Metal shaders: fields of dots that roll like waves and flow into living patterns, all drawn on the GPU.
A deep-space starfield in Metal: glowing nebulae drift past twinkling stars.
Deep-space starfield
Free
A deep-space starfield in Metal: glowing nebulae drift past twinkling stars.
Neon outlines that bloom from the center with prism-colored edges, rendered live in Metal.
Neon outlines
Free
Neon outlines that bloom from the center with prism-colored edges, rendered live in Metal.
Colored blobs that drift, melt into one another like liquid, and blend their colors as they merge.
Liquid metaballs
Free
Colored blobs that drift, melt into one another like liquid, and blend their colors as they merge.
A 3D network graph you can spin. Tap any node and its links light up, with the details below.
3D network graph
Free
A 3D network graph you can spin. Tap any node and its links light up, with the details below.
Items orbit into a dotted ring, and payment cards fan out of a wallet as the balance rolls in.
Orbit ring and wallet
Free
Items orbit into a dotted ring, and payment cards fan out of a wallet as the balance rolls in.
A field of bright colored dots that float and shimmer across the whole screen.
Floating color dots
Free
A field of bright colored dots that float and shimmer across the whole screen.
Stacked glass panels in mirrored layers, with neon gradients flowing through them.
Glass color panels
Free
Stacked glass panels in mirrored layers, with neon gradients flowing through them.
Drag the slider and scattered points of light pull together into a perfect 3D sphere.
Dot sphere
Free
Drag the slider and scattered points of light pull together into a perfect 3D sphere.
Living patterns: a mosaic of cells that keeps reshaping, then a hypnotic color swirl.
Cells and swirl
Free
Living patterns: a mosaic of cells that keeps reshaping, then a hypnotic color swirl.
Organic noise shaders: glowing veins that pulse through the dark, then bold color blobs in constant flow.
Noise veins and blobs
Free
Organic noise shaders: glowing veins that pulse through the dark, then bold color blobs in constant flow.