-
실시간 현황 기초
실시간 현황으로 앱 경험을 향상하세요. iPhone을 가로 방향으로 사용할 때 더 많은 정보를 제공하는 Dynamic Island의 새로운 스타일 등 실시간 현황이 표시되는 다양한 위치를 살펴보세요. 각 공간에 맞게 실시간 현황을 맞춤화하고, 콘텐츠와 데이터를 구성하며, ActivityKit과 푸시 알림을 사용하여 시작부터 끝까지 실시간 업데이트를 진행하는 방법을 알아보세요.
챕터
- 0:01 - Introduction
- 1:53 - Create and update
- 9:51 - Optimize
리소스
- Human Interface Guidelines: Live Activities
- Starting and updating Live Activities with ActivityKit push notifications
- ActivityKit
관련 비디오
WWDC24
WWDC23
-
비디오 검색…
-
-
4:16 - Define initial Live Activity
// Define initial Live Activity. import ActivityKit import Foundation public struct DrinkOrderAttributes: ActivityAttributes { let shopName: String let drink: Drink let orderID: UUID public struct ContentState: Codable, Hashable { var phase: DrinkOrder.Phase = .waiting var estimatedReadyDate: Date var rating: DrinkOrder.Rating? } } -
5:35 - Create each Live Activity view
// Create each Live Activity view import ActivityKit import SwiftUI import WidgetKit struct DrinkOrderLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: DrinkOrderAttributes.self) { context in ActivityView(context: context) } dynamicIsland: { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { ExpandedLeadingView(context: context) } DynamicIslandExpandedRegion(.center) { ExpandedCenterView(context: context) } DynamicIslandExpandedRegion(.trailing) { ExpandedTrailingView(context: context) } DynamicIslandExpandedRegion(.bottom) { ExpandedBottomView(context: context) } } compactLeading: { CompactLeadingView(context: context) } compactTrailing: { CompactTrailingView(context: context) } minimal: { MinimalView(context: context) } } } } -
7:43 - Start and update a Live Activity
// Start a Live Activity func launchLiveActivity(order: DrinkOrder) throws { guard ActivityAuthorizationInfo().areActivitiesEnabled else { return } let attributes = DrinkOrderAttributes(shopName: "Coffee Shop", drink: order.drink, orderID: order.id) let estimatedReadyDate = Date.now + (15 * 60) let contentState = DrinkOrderAttributes.ContentState(phase: .waiting, estimatedReadyDate: estimatedReadyDate) let activityContent = ActivityContent(state: contentState, staleDate: nil) let activity = try Activity.request(attributes: attributes, content: activityContent) } // Update a Live Activity await activity.update( ActivityContent( state: DrinkOrderAttributes.ContentState( phase: .preparing, estimatedReadyDate: estimatedReadyDate ), staleDate: nil ) ) -
10:33 - Optimize for limited width in the Dynamic Island
// Optimize for limited width in the Dynamic Island struct CompactTrailingView: View { @Environment(\.isDynamicIslandLimitedInWidth) var isDynamicIslandLimitedInWidth var context: ActivityViewContext<DrinkOrderAttributes> var body: some View { if isDynamicIslandLimitedInWidth { StepProgressIconView(context: context) } else if context.state.phase.showsTimer { EstimatedReadyView(context: context, font: .system(.body).monospacedDigit()) .multilineTextAlignment(.trailing) .frame(maxWidth: maximumTimerLabelWidth) } else { OrderPhaseLabelView(context: context, font: .caption2.bold(), color: .brown) .multilineTextAlignment(.trailing) } } } -
11:34 - Extend background color in StandBy
// Extend background color in StandBy struct ActivityView: View { @Environment(\.showsWidgetContainerBackground) var showsWidgetContainerBackground var context: ActivityViewContext<DrinkOrderAttributes> var body: some View { DetailView(context: context) .background { if showsWidgetContainerBackground { LinearGradient.barista } } .activityBackgroundTint(.espresso) } } -
12:30 - Add support for activityFamily small
// Add support for activityFamily small import ActivityKit import SwiftUI import WidgetKit struct DrinkOrderLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: DrinkOrderAttributes.self) { context in ActivityView(context: context) } dynamicIsland: { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { ExpandedLeadingView(context: context) } DynamicIslandExpandedRegion(.center) { ExpandedCenterView(context: context) } DynamicIslandExpandedRegion(.trailing) { ExpandedTrailingView(context: context) } DynamicIslandExpandedRegion(.bottom) { ExpandedBottomView(context: context) } } compactLeading: { CompactLeadingView(context: context) } compactTrailing: { CompactTrailingView(context: context) } minimal: { MinimalView(context: context) } } .supplementalActivityFamilies([.small]) } } -
12:43 - Optimize for small family
// Optimize for small family struct ActivityView: View { @Environment(\.showsWidgetContainerBackground) var showsWidgetContainerBackground @Environment(\.activityFamily) var activityFamily var context: ActivityViewContext<DrinkOrderAttributes> var body: some View { contentView .background { if showsWidgetContainerBackground { LinearGradient.barista } } .activityBackgroundTint(.espresso) } @ViewBuilder var contentView: some View { if activityFamily == .small { SmallView(context: context) } else { DetailView(context: context) } } } -
13:36 - Add interactivity with App Intents
// Add interactivity with App Intents struct RateDrinkIntent: LiveActivityIntent { static var title: LocalizedStringResource = "Rate Drink" @Parameter(title: "Order ID") var orderID: String @Parameter(title: "Positive") var isPositive: Bool func perform() async throws -> some IntentResult { await updateLocalDatastore(rating: isPositive ? .great : .poor, dismissPolicy: .after(.now + 15)) return .result() } } -
14:06 - Associate an intent with a button
// Associate an intent with a button struct RatingButtons: View { var context: ActivityViewContext<DrinkOrderAttributes> var body: some View { HStack(spacing: 12) { Button(intent: RateDrinkIntent( orderID: context.attributes.orderID.uuidString, isPositive: false)) { Label("Not Good", systemImage: "hand.thumbsdown.fill") } .buttonStyle(RatingButtonStyle(color: .red)) Button(intent: RateDrinkIntent( orderID: context.attributes.orderID.uuidString, isPositive: true)) { Label("Great", systemImage: "hand.thumbsup.fill") } .buttonStyle(RatingButtonStyle(color: .green)) } } }
-
-
- 0:01 - Introduction
Live Activities keep people up-to-date about ongoing tasks or events with progressing information over time, appearing on the Lock Screen, in the Dynamic Island, on Apple Watch, and on the CarPlay Dashboard.
- 1:53 - Create and update
Start by defining an efficient data model using ActivityAttributes for static data and ContentState for dynamic data. Construct tailored UI for each presentation and manage the activity's lifecycle locally via ActivityKit or remotely using push notifications.
- 9:51 - Optimize
Refine the Live Activity experience by adapting layouts to accommodate specific constraints, such as limited width in the Dynamic Island in landscape, or adopting the small activity family for Apple Watch. Integrate App Intents to let people perform quick, contextual actions.