Developing Dynamic Island Integration for iOS
Dynamic Island — interactive zone around Face ID notch on iPhone 14 Pro and newer. Technically part of Live Activities API: without Live Activity Dynamic Island doesn't work. But designing good Dynamic Island integration — separate task with specific UX constraints and technical nuances.
Three States of Dynamic Island
Minimal — when multiple active Live Activities on device. Your Activity competes with others. System shows one icon right or left of "island". Content — one icon or very short text. User taps — goes to Compact or Expanded.
Compact — standard collapsed state. Two zones: compactLeading (left part of notch) and compactTrailing (right). Available width — roughly 100pt each zone, height — 36pt. Don't try to fit much: icon + short number — maximum.
Expanded — long tap by user. Expands to large card. Four zones: .leading, .trailing, .center, .bottom. Bottom zone largest — main content goes there.
DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
// Status icon
Image(systemName: orderStatusIcon)
.font(.title2)
.foregroundColor(.orange)
}
DynamicIslandExpandedRegion(.trailing) {
// Time
Label("\(minutesLeft) min", systemImage: "clock")
.font(.caption)
}
DynamicIslandExpandedRegion(.center) {
// Center content
Text(restaurantName)
.font(.headline)
.lineLimit(1)
}
DynamicIslandExpandedRegion(.bottom) {
// Main action
HStack {
ProgressView(value: deliveryProgress)
.tint(.orange)
Text(statusText)
.font(.subheadline)
}
.padding(.horizontal)
}
} compactLeading: {
Image(systemName: "bag.fill").foregroundColor(.orange)
} compactTrailing: {
Text("\(minutesLeft)m").font(.caption2).bold()
} minimal: {
Image(systemName: "bag.fill")
}
.keyframeAnimator(initialValue: AnimationValues()) { content, value in
content.scaleEffect(value.scale)
} keyframes: { _ in
KeyframeTrack(\.scale) {
LinearKeyframe(1.0, duration: 0.1)
SpringKeyframe(1.1, duration: 0.3)
SpringKeyframe(1.0, duration: 0.2)
}
}
keyframeAnimator (iOS 17+) — animation on ContentState update. Pulsing icon on status change — exactly like this. Without animation update just "flickers".
Transition from Dynamic Island to App
Tap on Dynamic Island in Expanded state opens app. widgetURL(_:) in View sets URL for deep link. In app — onContinueUserActivity(_:perform:) or onOpenURL in SwiftUI.
Important: different Expanded zones can lead to different deep link URLs. Left zone — to map with courier, bottom — to order details.
Animations on Appearance and Update
When Live Activity starts Dynamic Island "grows" with animation. On ContentState update — transition between states animated by system, but can add custom animation via withAnimation in View.
Change of compactLeading/compactTrailing content on update: system applies crossfade. For more controlled transition — ContentTransition.numericText() for numeric values (time, score), ContentTransition.identity for content replacement without animation.
What's Not Possible and Often Forgotten
Can't start without active Activity<T> — Dynamic Island exists only while Live Activity lives. No Live Activity — no Dynamic Island.
Can't show arbitrary content in Minimal without Activity. Can't control position (left/right in Minimal) — system decides.
Compact zones not interactive themselves — tap on Compact opens Expanded, then tap in Expanded leads by widgetURL. Can't make button directly in Compact.
Testing on simulator: Dynamic Island simulated as "flat" zone, animations simplified. Final appearance and behavior — only on real iPhone 14 Pro/Pro Max.
Timeframe: 3-5 days with existing Live Activity. If Live Activity doesn't exist — need to add its development. Cost calculated after requirements analysis.







