-
Fondamentaux d’Activités en direct
Améliorez l'expérience de votre app avec Activités en direct. Explorez de nombreux endroits où apparaissent les Activités en direct, notamment un nouveau style dans Dynamic Island qui fournit plus d'informations lorsque l'iPhone est utilisé en mode paysage. Apprenez à adapter votre Activité en direct à chaque espace, à structurer votre contenu et vos données, et à assurer des mises à jour complètes en temps réel à l'aide d'ActivityKit et des notifications push.
Chapitres
- 0:01 - Introduction
- 1:53 - Create and update
- 9:51 - Optimize
Ressources
- Human Interface Guidelines: Live Activities
- Starting and updating Live Activities with ActivityKit push notifications
- ActivityKit
Vidéos connexes
WWDC24
WWDC23
-
Rechercher dans cette vidéo…
-
-
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.