SwiftUI transition that expands from the iPhone notch / Dynamic Island into a full-screen view.
iOS 16.4+ with Metal shader effects on iOS 17+.
- Starts from the notch / Dynamic Island area.
- Expands into a rounded black shell.
- Can show a shader-driven inner card before your destination view appears.
- Dismisses back out with the shader hidden by default.
- Adapts sizing for Dynamic Island devices, notch devices, older iPhones, and iPad.
Add the package:
dependencies: [
.package(url: "https://github.com/kovs705/NotchTransition.git", branch: "main")
]Then add NotchTransition to your target dependencies.
import SwiftUI
import NotchTransition
struct ContentView: View {
@State private var showTransition = false
var body: some View {
Button("Show Transition") {
showTransition = true
}
.notchTransition(isPresented: $showTransition) {
VStack(spacing: 24) {
Text("Hello from NotchTransition")
.font(.largeTitle.bold())
.foregroundStyle(.white)
Button("Close") {
showTransition = false
}
.buttonStyle(.borderedProminent)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.black)
}
}
}The easiest API is the direct modifier overload where the important options are visible in the call site:
.notchTransition(
isPresented: $showTransition,
animationTimings: .default,
backgroundColor: .black,
material: .ultraThinMaterial,
innerViewStyle: .gradientFill,
innerSurfaceLayout: .screenshot,
showsInnerViewOnDismiss: false
) {
DestinationView()
}animationTimings: transition speed preset or custom timings.backgroundColor: color of the outer expanding shell.material: optional backdrop material shown when fullscreen.innerViewStyle: shader or fill used for the inner card.innerSurfaceLayout: spacing of the inner card inside the black shell during the rectangle phase.showsInnerViewOnDismiss: whether the shader card becomes visible again while dismissing. Default isfalse.
.notchTransition(
isPresented: $showSlow,
animationTimings: .slow
) {
SlowView()
}.notchTransitionThemed(
isPresented: $showThemed,
backgroundColor: .cyan,
material: .ultraThinMaterial
) {
ThemedView()
}.notchTransitionSlow(isPresented: $showSlow) {
SlowView()
}InnerViewStyle controls what appears inside the rounded inner card before your destination content fades in.
.sinebow
.lightGrid
.gradientFill
.plain(.blue)
.invisibleNotes:
- On iOS 17+,
sinebow,lightGrid, andgradientFilluse Metal shaders. - On iOS 16.4 to iOS 16.x, those styles fall back to an animated gradient.
.invisiblekeeps only the outer black shell.
InnerSurfaceLayout controls the screenshot-style spacing of the shader card inside the expanding black shell.
Available presets:
.compact
.screenshot
.immersiveCustom layout:
let layout = TransitionConfiguration.InnerSurfaceLayout(
horizontalInset: 14,
topPadding: 10,
bottomPadding: 14
)
.notchTransition(
isPresented: $showTransition,
innerSurfaceLayout: layout
) {
DestinationView()
}By default, the inner shader card stays hidden during dismiss:
.notchTransition(
isPresented: $showTransition,
showsInnerViewOnDismiss: false
) {
DestinationView()
}If you want the shader card to reappear while dismissing:
.notchTransition(
isPresented: $showTransition,
showsInnerViewOnDismiss: true
) {
DestinationView()
}If you want to construct and reuse one configuration, that still works:
let config = TransitionConfiguration(
animationTimings: .slow,
backgroundColor: .indigo,
backgroundMaterial: .regularMaterial,
innerViewStyle: .gradientFill,
innerSurfaceLayout: .screenshot,
showsInnerViewOnDismiss: false
)
.notchTransition(
isPresented: $showTransition,
configuration: config
) {
DestinationView()
}There are also fluent helpers on TransitionConfiguration:
let config = TransitionConfiguration.default
.innerViewStyle(.lightGrid)
.innerSurfaceLayout(.compact)
.showsInnerViewOnDismiss(true)Use them if you want. The direct modifier overload is usually easier to read.
- Hidden -> Notch
- Notch -> Rectangle
- Rectangle -> Fullscreen
- Destination content fades in
On dismiss, destination content fades out first, then the shell exits. The inner shader is hidden by default during that dismiss path.
| Device Type | Behavior |
|---|---|
| Dynamic Island iPhones | Uses native Dynamic Island dimensions |
| Notch iPhones | Uses scaled notch dimensions |
| Older iPhones | Simulates a notch-origin transition |
| iPad | Uses larger tablet sizing |
- iOS 16.4+
- Xcode 15+
- Swift 5.9+
The package includes example views in:
Sources/NotchTransition/Example/NotchTransitionExample.swift
This package includes Metal shader code derived from Inferno by Paul Hudson and contributors.
- Upstream project:
twostraws/Inferno - License: MIT
- Source:
https://github.com/twostraws/Inferno
At the time of writing, Inferno is distributed under the MIT License, with copyright attributed to Paul Hudson and other authors.
MIT. See LICENSE.