-
Fundamentos das Atividades ao Vivo
Eleve a experiência no seu app com as Atividades ao Vivo. Conheça os diversos ambientes em que as Atividades ao Vivo são exibidas, incluindo um novo estilo na Dynamic Island que oferece mais detalhes quando o iPhone é utilizado no modo paisagem. Aprenda a adaptar sua Atividade ao Vivo para cada espaço, estruturar seu conteúdo e dados e gerar atualizações em tempo real do início ao fim usando o ActivityKit e notificações por push.
Capítulos
- 0:01 - Introduction
- 1:53 - Create and update
- 9:51 - Optimize
Recursos
- Human Interface Guidelines: Live Activities
- Starting and updating Live Activities with ActivityKit push notifications
- ActivityKit
Vídeos relacionados
WWDC24
WWDC23
-
Buscar neste vídeo...
-
-
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.