Розробка Dynamic Island інтеграції для iOS
Dynamic Island — інтерактивна зона навколо Face ID вирізки на iPhone 14 Pro та новішому. Технічно це частина Live Activities API: без Live Activity Dynamic Island не працює. Але спроектувати хорошу Dynamic Island інтеграцію — окрема задача з конкретними UX-обмеженнями та технічними тонкощами.
Три стани Dynamic Island
Minimal — коли на устройстве кілька активних Live Activities одночасно. Ваша Activity конкурує з іншими. Система показує одну іконку справа або зліва від «острова». Контент — одна іконка або дуже короткий текст. Користувач натискає — переходить в Compact або Expanded.
Compact — стандартне свернуте стан. Дві зони: compactLeading (ліва частина вирізки) та compactTrailing (права). Доступна ширина — приблизно 100pt на кожну зону, висота — 36pt. Не намагайтеся втиснути багато: іконка + короткий номер — максимум.
Expanded — довгий тап користувача. Розгортається до великої карточки. Чотири зони: .leading, .trailing, .center, .bottom. Bottom-зона найбільша — туди йде основний контент.
DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
// Статус іконка
Image(systemName: orderStatusIcon)
.font(.title2)
.foregroundColor(.orange)
}
DynamicIslandExpandedRegion(.trailing) {
// Час
Label("\(minutesLeft) хв", systemImage: "clock")
.font(.caption)
}
DynamicIslandExpandedRegion(.center) {
// Центральний контент
Text(restaurantName)
.font(.headline)
.lineLimit(1)
}
DynamicIslandExpandedRegion(.bottom) {
// Основне дія
HStack {
ProgressView(value: deliveryProgress)
.tint(.orange)
Text(statusText)
.font(.subheadline)
}
.padding(.horizontal)
}
} compactLeading: {
Image(systemName: "bag.fill").foregroundColor(.orange)
} compactTrailing: {
Text("\(minutesLeft)хв").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+) — анімація при оновленні ContentState. Пульсуючи іконка при зміні статусу — саме так. Без анімації оновлення просто «миготить».
Перехід з Dynamic Island в додаток
Натиск на Dynamic Island у Expanded стані відкриває додаток. widgetURL(_:) у View задає URL для deep link. У додатку — onContinueUserActivity(_:perform:) або onOpenURL в SwiftUI.
Важливо: різні зони Expanded можуть вести на різні deep link URLs. Ліва зона — на карту з курьєром, нижня — в деталі замовлення.
Анімації при появленні та оновленні
При старті Live Activity Dynamic Island «виростає» з анімацією. При оновленні ContentState — перехід між станами анімується системою, але можна додати кастомну анімацію через withAnimation у View.
Смена compactLeading/compactTrailing контенту при оновленні: система застосовує crossfade. Для більш контрольованого переходу — ContentTransition.numericText() для числових значень (час, рахунок), ContentTransition.identity для заміни контенту без анімації.
Що не можна та що часто забувають
Не можна запустити без активного Activity<T> — Dynamic Island існує лише поки Live Activity жива. Нема Live Activity — нема Dynamic Island.
Не можна показувати довільний контент у Minimal без Activity. Не можна керувати позицією (зліва/справа в Minimal) — система вирішує сама.
Compact-зони не інтерактивні самі по собі — натиск на Compact відкриває Expanded, потім натиск у Expanded веде по widgetURL. Зробити кнопку прямо в Compact не отримається.
Тестування на симуляторі: Dynamic Island симулюється як «плоска» зона, анімації упрощены. Фінальний вид та поведінка — лише на реальному iPhone 14 Pro/Pro Max.
Термін: 3-5 днів при наявності готової Live Activity. Якщо Live Activity ще нема — потрібно додати її розробку. Вартість розраховується після аналізу вимог.







