-
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 - Introdução
- 1:53 - Crie e atualize
- 9:51 - Otimize
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)) } } }
-