iOS16靈動(dòng)島和鎖屏實(shí)時(shí)活動(dòng)

使用實(shí)時(shí)活動(dòng)顯示實(shí)時(shí)數(shù)據(jù)

提供實(shí)時(shí)活動(dòng),在靈動(dòng)島和鎖定屏幕上顯示您的應(yīng)用程序的最新數(shù)據(jù)。

概述

實(shí)時(shí)活動(dòng)在 iPhone 鎖定屏幕和靈動(dòng)島中顯示您的應(yīng)用程序的最新數(shù)據(jù)。這使人們可以一目了然地看到實(shí)時(shí)信息。

要提供實(shí)時(shí)活動(dòng),請(qǐng)將代碼添加到您現(xiàn)有的小部件擴(kuò)展或創(chuàng)建一個(gè)新的小部件擴(kuò)展(如果您的應(yīng)用尚未包含)。Live Activity 使用WidgetKit功能和SwiftUI繪制用戶界面。ActivityKit 的作用是處理每個(gè) Live Activity 的生命周期:您使用它的 API 來請(qǐng)求、更新和結(jié)束 Live Activity。

筆記
現(xiàn)場(chǎng)活動(dòng)僅在 iPhone 上可用。

要提供實(shí)時(shí)活動(dòng)( Live Activity)代碼需要添加到小組件中(iOS14 以后的widget)使用WidgetKit功能,使用SwiftUI繪制界面。iOS16新增了ActivityKit,其作用是處理每個(gè) Live Activity 的生命周期:您使用它的 API 來請(qǐng)求、更新和結(jié)束 Live Activity。

查看實(shí)時(shí)活動(dòng)要求和限制

除非您的應(yīng)用程序或用戶結(jié)束它,否則 Live Activity 最多可以處于活動(dòng)狀態(tài)八小時(shí)。超過此限制,系統(tǒng)自動(dòng)結(jié)束。當(dāng) Live Activity 結(jié)束時(shí),系統(tǒng)會(huì)立即將其從 Dynamic Island 中移除。但是,實(shí)時(shí)活動(dòng)會(huì)一直保留在鎖定屏幕上,直到用戶將其刪除或系統(tǒng)將其刪除前最多四個(gè)小時(shí)(以先到者為準(zhǔn))。因此,實(shí)時(shí)活動(dòng)會(huì)在鎖定屏幕上最多保留十二小時(shí)。

有關(guān)結(jié)束 Live Activity 的更多信息,請(qǐng)參閱下面的“在您的應(yīng)用內(nèi)結(jié)束 Live Activity”。

每個(gè) Live Activity 在自己的沙箱中運(yùn)行,并且 - 與小部件不同 - 它無法訪問網(wǎng)絡(luò)或接收位置更新。要更新活動(dòng) Live Activity 的動(dòng)態(tài)數(shù)據(jù),請(qǐng)?jiān)谀膽?yīng)用程序中使用 ActivityKit 框架或允許您的 Live Activity 接收遠(yuǎn)程推送通知,如使用遠(yuǎn)程推送通知更新和結(jié)束您的 Live Activity 中所述。

筆記
ActivityKit 更新和遠(yuǎn)程推送通知更新的更新動(dòng)態(tài)數(shù)據(jù)大小不能超過 4KB。

實(shí)時(shí)活動(dòng)針對(duì)鎖定屏幕和靈動(dòng)島提供不同的視圖。鎖定屏幕視圖出現(xiàn)在所有設(shè)備上。支持靈動(dòng)島的設(shè)備使用以下視圖顯示實(shí)時(shí)活動(dòng):靈動(dòng)島的緊湊前視圖、緊湊尾視圖、最小視圖和擴(kuò)展視圖。
當(dāng)人在靈動(dòng)島中觸摸并持有緊湊或最小視圖以及實(shí)時(shí)活動(dòng)更新時(shí),會(huì)出現(xiàn)擴(kuò)展視圖。在不支持靈動(dòng)島的解鎖設(shè)備上,展開的視圖顯示為實(shí)時(shí)活動(dòng)更新的橫幅。
為確保系統(tǒng)可以在每個(gè)位置顯示您的 Live Activity,您必須支持所有視圖。

為您的應(yīng)用添加對(duì)實(shí)時(shí)活動(dòng)的支持

描述 Live Activity 用戶界面的代碼是應(yīng)用的小部件擴(kuò)展的一部分。如果您已經(jīng)在應(yīng)用程序中提供小部件,則可以將 Live Activity 的用戶界面代碼添加到現(xiàn)有的小部件擴(kuò)展中,并且可以在小部件和 Live Activity 之間重用代碼。然而,盡管 Live Activity 利用了 WidgetKit 的功能,但它們并不是小部件。與您用于更新小部件用戶界面的時(shí)間線機(jī)制相比,您可以使用 ActivityKit 或遠(yuǎn)程推送通知從您的應(yīng)用程序更新實(shí)時(shí)活動(dòng)。

筆記
您可以創(chuàng)建一個(gè)小部件擴(kuò)展來采用實(shí)時(shí)活動(dòng),而無需提供小部件。但是,請(qǐng)考慮同時(shí)提供小部件和實(shí)時(shí)活動(dòng),以允許人們?cè)谥髌聊缓玩i定屏幕上添加可瀏覽的信息和個(gè)人風(fēng)格。

為您的應(yīng)用添加對(duì)實(shí)時(shí)活動(dòng)的支持:

  1. 如果您還沒有向您的應(yīng)用程序添加一個(gè)小部件擴(kuò)展,請(qǐng)創(chuàng)建一個(gè)小部件擴(kuò)展。有關(guān)創(chuàng)建小部件擴(kuò)展的更多信息,請(qǐng)參閱WidgetKit創(chuàng)建小部件擴(kuò)展

  2. 打開應(yīng)用程序的 Info.plist 文件,添加 Supports Live Activities 條目,并將其布爾值設(shè)置為 YES。或者,將 Info.plist 文件作為源代碼打開,添加鍵 NSSupportsLiveActivities,然后將類型設(shè)置為布爾值并將其值設(shè)置為 YES。如果您的項(xiàng)目沒有Info.plist 文件,請(qǐng)將條目添加到您的 iOS 應(yīng)用程序目標(biāo)的自定義 iOS 目標(biāo)屬性列表中。

  3. 添加定義ActivityAttributes結(jié)構(gòu)的代碼以描述 Live Activity 的靜態(tài)和動(dòng)態(tài)數(shù)據(jù).

  4. 使用您定義的 ActivityAttributes創(chuàng)建開始實(shí)時(shí)活動(dòng)所需的ActivityConfiguration。

  5. 添加代碼以配置、啟動(dòng)、更新和結(jié)束您的實(shí)時(shí)活動(dòng)。

定義一組靜態(tài)和動(dòng)態(tài)數(shù)據(jù)

在為 Live Activity 創(chuàng)建配置對(duì)象之前,通過實(shí)現(xiàn) ActivityAttributes 來描述 Live Activity 顯示的數(shù)據(jù)。ActivityAttributes 通知系統(tǒng)有關(guān) Live Activity 中出現(xiàn)的靜態(tài)數(shù)據(jù)。您還可以使用 ActivityAttributes 來聲明所需的自定義 Activity.ContentState 類型,該類型描述您的 Live Activity 的動(dòng)態(tài)數(shù)據(jù)。在下面的示例中,PizzaDeliveryAttributes 描述了以下靜態(tài)數(shù)據(jù):訂購(gòu)的比薩餅數(shù)量、客戶需要支付的金額以及訂單號(hào)。請(qǐng)注意代碼如何定義 Activity.ContentState 來封裝動(dòng)態(tài)數(shù)據(jù):送披薩的司機(jī)的姓名和預(yù)計(jì)送達(dá)時(shí)間。此外,該示例定義了類型別名 PizzaDeliveryStatus 以使代碼更具描述性和易于閱讀。

import Foundation
import ActivityKit

struct PizzaDeliveryAttributes: ActivityAttributes {
    public typealias PizzaDeliveryStatus = ContentState

    public struct ContentState: Codable, Hashable {
        var driverName: String
        var deliveryTimer: ClosedRange<Date>
    }

    var numberOfPizzas: Int
    var totalAmount: String
    var orderNumber: String
}

為您的實(shí)時(shí)活動(dòng)創(chuàng)建配置

添加代碼以使用 ActivityAttributes 結(jié)構(gòu)來描述 Live Activity 中顯示的數(shù)據(jù)后,添加代碼以在小部件實(shí)現(xiàn)中返回 ActivityConfiguration。以下示例使用上一個(gè)示例中的 PizzaDeliveryAttributes 結(jié)構(gòu)來配置您的 Live Activity。

import SwiftUI
import WidgetKit

@main
struct PizzaDeliveryActivityWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: PizzaDeliveryAttributes.self) { context in
            // Create the view that appears on the Lock Screen and as a
            // banner on the Home Screen of devices that don't support the
            // Dynamic Island.
            // ...
        } dynamicIsland: { context in
            // Create the views that appear in the Dynamic Island.
            // ...
        }
    }
}

如果您的應(yīng)用已經(jīng)提供小部件,請(qǐng)將 Live Activity 添加到您的 WidgetBundle。如果您沒有 WidgetBundle(例如,如果您只提供一個(gè)小部件),請(qǐng)按照創(chuàng)建小部件擴(kuò)展中的描述創(chuàng)建一個(gè)小部件包,然后將 Live Activity 添加到其中。以下示例顯示了如何使用帶有可用性子句的 if 語句僅在設(shè)備支持 Live Activity 的情況下將 Live Activity 添加到小部件捆綁包中:

@main
struct PizzaDeliveryWidgets: WidgetBundle {
    var body: some Widget {
        FavoritePizzaWidget()

        if #available(iOS 16.1, *) {
            PizzaDeliveryLiveActivity()
        }
    }
}

創(chuàng)建鎖定屏幕視圖

要?jiǎng)?chuàng)建 Live Activity 的用戶界面,您可以在之前創(chuàng)建的小部件擴(kuò)展中使用SwiftUI。與小部件類似,您無需為 Live Activity 提供用戶界面的大小,而是讓系統(tǒng)確定合適的尺寸。

從鎖定屏幕上顯示的視圖開始。以下代碼顯示PizzaDeliveryAttributes結(jié)構(gòu)使用標(biāo)準(zhǔn) SwiftUI 視圖描述的信息:

@main
struct PizzaDeliveryWidget: Widget {    
    var body: some WidgetConfiguration { 
        ActivityConfiguration(for: PizzaDeliveryAttributes.self) { context in
      // Create the view that appears on the Lock Screen and as a
            // banner on the Home Screen of devices that don't support the
            // Dynamic Island.
            LockScreenLiveActivityView(context: context)
        } dynamicIsland: { context in
            // Create the views that appear in the Dynamic Island.
            // ...
        }
    }
}

struct LockScreenLiveActivityView: View {
    let context: ActivityViewContext<PizzaDeliveryAttributes>
    
    var body: some View {
        VStack {
            Spacer()
            Text("\(context.state.driverName) is on their way with your pizza!")
            Spacer()
            HStack {
                Spacer()
                Label {
                    Text("\(context.attributes.numberOfPizzas) Pizzas")
                } icon: {
                    Image(systemName: "bag")
                        .foregroundColor(.indigo)
                }
                .font(.title2)
                Spacer()
                Label {
                    Text(timerInterval: context.state.deliveryTimer, countsDown: true)
                        .multilineTextAlignment(.center)
                        .frame(width: 50)
                        .monospacedDigit()
                } icon: {
                    Image(systemName: "timer")
                        .foregroundColor(.indigo)
                }
                .font(.title2)
                Spacer()
            }
            Spacer()
        }
        .activitySystemActionForegroundColor(.indigo)
        .activityBackgroundTint(.cyan)
    }
}

筆記
如果其高度超過 160 點(diǎn),系統(tǒng)可能會(huì)截?cái)噫i定屏幕上的實(shí)時(shí)活動(dòng)。

創(chuàng)建緊湊和最小的視圖

實(shí)時(shí)活動(dòng)出現(xiàn)在支持它的設(shè)備的靈動(dòng)島中。當(dāng)您啟動(dòng)一個(gè) Live Activity 并且它是唯一一個(gè)活躍的 Live Activity 時(shí),緊湊的前導(dǎo)視圖和尾隨視圖一起出現(xiàn),以在 Dynamic Island 中形成一個(gè)有凝聚力的視圖。當(dāng)多個(gè) Live Activity 處于活動(dòng)狀態(tài)時(shí)(無論是來自您的應(yīng)用程序還是來自多個(gè)應(yīng)用程序),系統(tǒng)會(huì)選擇哪些 Live Activity 可見并使用每個(gè)最小視圖顯示兩個(gè):一個(gè)最小視圖顯示為附加到靈動(dòng)島,而另一個(gè)顯示為分離.

筆記
默認(rèn)情況下,Dynamic Island 中的緊湊和最小視圖使用黑色背景顏色和白色文本。使用keylineTint(_:)修改器將可選的色調(diào)應(yīng)用到靈動(dòng)島 - 例如,應(yīng)用青色,如下例所示。
以下示例展示了披薩外賣應(yīng)用程序如何使用標(biāo)準(zhǔn) SwiftUI 視圖提供所需的緊湊和最小視圖:

import SwiftUI
import WidgetKit

@main
struct PizzaDeliveryWidget: Widget {    
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: PizzaDeliveryAttributes.self) { context in
            // Create the view that appears on the Lock Screen and as a
            // banner on the Home Screen of devices that don't support the
            // Dynamic Island.
            // ...
        } dynamicIsland: { context in
            // Create the views that appear in the Dynamic Island.
            DynamicIsland {
                // Create the expanded view.
                // ...
                
            } compactLeading: {
                Label {
                    Text("\(context.attributes.numberOfPizzas) Pizzas")
                } icon: {
                    Image(systemName: "bag")
                        .foregroundColor(.indigo)
                }
                .font(.caption2)
            } compactTrailing: {
                Text(timerInterval: context.state.deliveryTimer, countsDown: true)
                    .multilineTextAlignment(.center)
                    .frame(width: 40)
                    .font(.caption2)
            } minimal: {
                VStack(alignment: .center) {
                    Image(systemName: "timer")
                    Text(timerInterval: context.state.deliveryTimer, countsDown: true)
                        .multilineTextAlignment(.center)
                        .monospacedDigit()
                        .font(.caption2)
                }
            }
            .keylineTint(.cyan)
        }
    }
}

創(chuàng)建擴(kuò)展視圖

除了緊湊和最小視圖之外,您還必須支持?jǐn)U展視圖。當(dāng)一個(gè)人觸摸并持有一個(gè)緊湊或最小的視圖時(shí),它會(huì)出現(xiàn),并且也會(huì)短暫出現(xiàn)以用于實(shí)時(shí)活動(dòng)更新。當(dāng)您更新實(shí)時(shí)活動(dòng)時(shí),沒有靈動(dòng)島的設(shè)備也會(huì)將擴(kuò)展視圖顯示為橫幅。使用DynamicIslandExpandedRegionPosition指定您希望 SwiftUI 將內(nèi)容放置在何處的詳細(xì)說明。以下示例顯示了披薩外賣應(yīng)用程序如何創(chuàng)建其擴(kuò)展視圖:

@main
struct PizzaDeliveryWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: PizzaDeliveryAttributes.self) { context in
            // Create the view that appears on the Lock Screen and as a
            // banner on the Home Screen of devices that don't support the
            // Dynamic Island.
            LockScreenLiveActivityView(context: context)
        } dynamicIsland: { context in
            // Create the views that appear in the Dynamic Island.
            DynamicIsland {
                // Create the expanded view.
                DynamicIslandExpandedRegion(.leading) {
                    Label("\(context.attributes.numberOfPizzas) Pizzas", systemImage: "bag")
                        .foregroundColor(.indigo)
                        .font(.title2)
                }
                
                DynamicIslandExpandedRegion(.trailing) {
                    Label {
                        Text(timerInterval: context.state.deliveryTimer, countsDown: true)
                            .multilineTextAlignment(.trailing)
                            .frame(width: 50)
                            .monospacedDigit()
                    } icon: {
                        Image(systemName: "timer")
                            .foregroundColor(.indigo)
                    }
                    .font(.title2)
                }
                
                DynamicIslandExpandedRegion(.center) {
                    Text("\(context.state.driverName) is on their way!")
                        .lineLimit(1)
                        .font(.caption)
                }
                
                DynamicIslandExpandedRegion(.bottom) {
                    Button {
                        // Deep link into your app.
                    } label: {
                        Label("Call driver", systemImage: "phone")
                    }
                    .foregroundColor(.indigo)
                }
            } compactLeading: {
                // Create the compact leading view.
                // ...
            } compactTrailing: {
                // Create the compact trailing view.
                // ...
            } minimal: {
                // Create the minimal view.
                // ...
            }
            .keylineTint(.yellow)
        }
    }
}

筆記
如果靈動(dòng)島的高度超過 160 點(diǎn),系統(tǒng)可能會(huì)截?cái)囔`動(dòng)島中的實(shí)時(shí)活動(dòng)。

為了呈現(xiàn)展開的 Live Activity 中出現(xiàn)的視圖,系統(tǒng)將展開的視圖劃分為不同的區(qū)域。請(qǐng)注意該示例如何返回一個(gè)指定多個(gè)DynamicIslandExpandedRegion 對(duì)象的DynamicIsland。傳遞以下值DynamicIslandExpandedRegionPosition以在展開視圖中的指定位置布置您的內(nèi)容:

  • center:將內(nèi)容放置在原深感攝像頭下方。

  • leading:將內(nèi)容沿展開的 Live Activity 的前沿放置在原深感攝像頭旁邊,并在其下方包裹其他內(nèi)容。

  • trailing:將內(nèi)容放置在 TrueDepth 攝像頭旁邊展開的 Live Activity 的后沿,并在其下方包裹其他內(nèi)容。

  • bottom:將內(nèi)容置于前導(dǎo)、尾隨和居中內(nèi)容之下。
    [圖片上傳失敗...(image-96c63b-1665454932342)]
    為了呈現(xiàn)展開的 Live Activity 中出現(xiàn)的內(nèi)容,<u>系統(tǒng)首先確定中心內(nèi)容的寬度,同時(shí)考慮前導(dǎo)和尾隨內(nèi)容的最小寬度</u>。然后系統(tǒng)根據(jù)其垂直位置放置前導(dǎo)和尾隨內(nèi)容并確定其大小。默認(rèn)情況下,前導(dǎo)視圖和尾隨視圖接收相同數(shù)量的水平空間。
    [圖片上傳失敗...(image-45ee62-1665454932342)]
    您可以通過將優(yōu)先級(jí)傳遞給init(_:priority:content:)初始化程序來告訴系統(tǒng)優(yōu)先考慮 DynamicIslandExpandedRegion 視圖之一。系統(tǒng)以動(dòng)態(tài)島的全寬呈現(xiàn)具有最高優(yōu)先級(jí)的視圖。

筆記
如果內(nèi)容太寬而無法出現(xiàn)在 TrueDepth 相機(jī)旁邊的前導(dǎo)位置,請(qǐng)使用belowIfTooWide修飾符來渲染 TrueDepth 相機(jī)下方的前導(dǎo)內(nèi)容。

使用自定義顏色

默認(rèn)情況下,系統(tǒng)使用默認(rèn)的文本原色和最適合用戶鎖定屏幕的 Live Activity 的背景顏色。要設(shè)置自定義色調(diào)顏色,請(qǐng)使用視圖修飾符activityBackgroundTint(_:)。此外,使用activitySystemActionForegroundColor(_:)視圖修飾符自定義允許人們?cè)阪i定屏幕上結(jié)束實(shí)時(shí)活動(dòng)的輔助按鈕的文本顏色。
要設(shè)置自定義背景色調(diào)顏色的半透明,請(qǐng)使用opacity(_:)視圖修改器或指定不透明背景顏色。

筆記
在包括 Always-On Retina 顯示屏的設(shè)備上,系統(tǒng)會(huì)調(diào)暗屏幕以延長(zhǎng)電池壽命,并在鎖定屏幕上呈現(xiàn)實(shí)時(shí)活動(dòng),就像在暗模式下一樣。使用 SwiftUI 的isLuminanceReduced環(huán)境值來檢測(cè) Always On 并使用在 Always On 中看起來很棒的圖像。

在您的應(yīng)用中創(chuàng)建深層鏈接

人們點(diǎn)擊實(shí)時(shí)活動(dòng)來啟動(dòng)您的應(yīng)用程序。為了改善用戶體驗(yàn),您可以使用widgetURL(_:)從鎖定屏幕、緊湊的前導(dǎo)、緊湊的尾隨和最少的視圖創(chuàng)建到您的應(yīng)用程序的深層鏈接。當(dāng)緊湊的前導(dǎo)視圖和尾隨視圖可見時(shí),請(qǐng)確保它們都鏈接到應(yīng)用程序中的同一屏幕。

擴(kuò)展視圖提供了額外的選項(xiàng)來創(chuàng)建應(yīng)用程序的深層鏈接,以便使用 SwiftUI 的Link.例如,披薩外賣應(yīng)用程序可能包含兩個(gè) SwiftUI 視圖。一個(gè)視圖可以在應(yīng)用程序中打開當(dāng)前送貨的地圖,第二個(gè)視圖可以打開一個(gè)屏幕,讓人們可以打電話給送披薩的人。

確保現(xiàn)場(chǎng)活動(dòng)可用

現(xiàn)場(chǎng)活動(dòng)僅在 iPhone 上可用。如果您的應(yīng)用可在多個(gè)平臺(tái)上使用并提供小部件擴(kuò)展,請(qǐng)確保實(shí)時(shí)活動(dòng)在運(yùn)行時(shí)可用。此外,用戶可以在“設(shè)置”應(yīng)用中選擇停用應(yīng)用的實(shí)時(shí)活動(dòng)。

要查看 Live Activity 是否可用以及用戶是否允許您的應(yīng)用使用 Live Activity:

  • 使用areActivitiesEnabled同步確定是否在您的應(yīng)用中顯示用戶界面以啟動(dòng) Live Activity。
  • 通過使用activityEnablementUpdates觀察流中的任何用戶授權(quán)更改來接收異步用戶授權(quán)更新并相應(yīng)地響應(yīng)它們。

筆記
一個(gè)應(yīng)用可以啟動(dòng)多個(gè) Live Activity,而一個(gè)設(shè)備可以從多個(gè)應(yīng)用運(yùn)行 Live Activity。除了確保 Live Activity 可用之外,在開始、更新或結(jié)束 Live Activity 時(shí)始終優(yōu)雅地處理任何錯(cuò)誤。例如,啟動(dòng) Live Activity 可能會(huì)失敗,因?yàn)橛脩舻脑O(shè)備可能已達(dá)到其活動(dòng) Live Activity 的限制。

開始現(xiàn)場(chǎng)活動(dòng)

當(dāng)應(yīng)用程序在前臺(tái)時(shí),您可以在應(yīng)用程序代碼中使用request(attributes:contentState:pushType:)函數(shù)啟動(dòng) Live Activity。它將您創(chuàng)建的屬性和內(nèi)容狀態(tài)作為參數(shù)來提供顯示在 Live Activity 中的初始值,并告訴系統(tǒng)哪些數(shù)據(jù)是動(dòng)態(tài)的。如果您實(shí)施遠(yuǎn)程推送通知來更新 Live Activity,還需要提供 pushType 參數(shù)。

以下代碼示例從前面的示例中為披薩外賣應(yīng)用啟動(dòng)了一個(gè)新的 Live Activity:

var future = Calendar.current.date(byAdding: .minute, value: (Int(minutes) ?? 0), to: Date())!
future = Calendar.current.date(byAdding: .second, value: (Int(seconds) ?? 0), to: future)!
let date = Date.now...future
let initialContentState = PizzaDeliveryAttributes.ContentState(driverName: "Bill James", deliveryTimer:date)
let activityAttributes = PizzaDeliveryAttributes(numberOfPizzas: 3, totalAmount: "$42.00", orderNumber: "12345")

do {
    deliveryActivity = try Activity.request(attributes: activityAttributes, contentState: initialContentState)
    print("Requested a pizza delivery Live Activity \(String(describing: deliveryActivity?.id)).")
} catch (let error) {
    print("Error requesting pizza delivery Live Activity \(error.localizedDescription).")
}

請(qǐng)注意上面的代碼片段如何不傳遞pushType參數(shù)并在不使用遠(yuǎn)程推送通知更新其內(nèi)容的情況下啟動(dòng) Live Activity。它還將返回的 Live Activity 對(duì)象存儲(chǔ)在可用于更新和結(jié)束 Live Activity 的deliveryActivity屬性中。有關(guān)使用遠(yuǎn)程推送通知更新您的實(shí)時(shí)活動(dòng)的更多信息,請(qǐng)參閱使用遠(yuǎn)程推送通知更新和結(jié)束您的實(shí)時(shí)活動(dòng)

筆記
您只能在應(yīng)用程序處于前臺(tái)時(shí)從應(yīng)用程序啟動(dòng)實(shí)時(shí)活動(dòng)。但是,您可以在應(yīng)用程序在后臺(tái)運(yùn)行時(shí)更新或結(jié)束 Live Activity - 例如,通過使用Background Tasks

更新實(shí)時(shí)活動(dòng)

當(dāng)您從應(yīng)用程序啟動(dòng) Live Activity 時(shí),使用您在啟動(dòng) Live Activity 時(shí)收到的Activity對(duì)象的update(using:)函數(shù)更新顯示在 Live Activity 中的數(shù)據(jù)。要檢索應(yīng)用程序的活動(dòng)實(shí)時(shí)活動(dòng),請(qǐng)使用activities.

例如,披薩配送應(yīng)用程序可以更新顯示配送狀態(tài)的 Live Activity,其中包含新的配送時(shí)間和新的司機(jī)。它還可以使用update(using:alertConfiguration:)函數(shù)在 iPhone 和 Apple Watch 上顯示警報(bào),告知人們新的 Live Activity 內(nèi)容,如下例所示:

var future = Calendar.current.date(byAdding: .minute, value: (Int(minutes) ?? 0), to: Date())!
future = Calendar.current.date(byAdding: .second, value: (Int(seconds) ?? 0), to: future)!
let date = Date.now...future
let updatedDeliveryStatus = PizzaDeliveryAttributes.PizzaDeliveryStatus(driverName: "Anne Johnson", deliveryTimer: date)
let alertConfiguration = AlertConfiguration(title: "Delivery Update", body: "Your pizza order will arrive in 25 minutes.", sound: .default)

await deliveryActivity?.update(using: updatedDeliveryStatus, alertConfiguration: alertConfiguration)

筆記
更新數(shù)據(jù)的大小不能超過 4KB。

在 Apple Watch 上,系統(tǒng)將titlebody屬性用于alert。在 iPhone 上,系統(tǒng)不會(huì)顯示常規(guī)alert,而是顯示靈動(dòng)島中展開的實(shí)時(shí)活動(dòng)。在不支持靈動(dòng)島的設(shè)備上,系統(tǒng)會(huì)在主屏幕上顯示一個(gè)橫幅,該橫幅使用您的實(shí)時(shí)活動(dòng)的擴(kuò)展視圖。

動(dòng)畫內(nèi)容更新

當(dāng)您定義 Live Activity 的用戶界面時(shí),系統(tǒng)會(huì)忽略任何動(dòng)畫修改器,例如withAnimation(::),和animation(_:value:),而是使用系統(tǒng)的動(dòng)畫時(shí)間。但是,當(dāng) Live Activity 的動(dòng)態(tài)內(nèi)容發(fā)生變化時(shí),系統(tǒng)會(huì)執(zhí)行一些動(dòng)畫。文本視圖通過模糊的內(nèi)容過渡動(dòng)畫內(nèi)容變化,并且系統(tǒng)為圖像和 SF 符號(hào)動(dòng)畫內(nèi)容過渡。如果您根據(jù)內(nèi)容或狀態(tài)更改從用戶界面添加或刪除視圖,視圖會(huì)淡入淡出。使用以下視圖轉(zhuǎn)換來配置這些內(nèi)置轉(zhuǎn)換:opacitymove(edge:)slidepush(from:)或它們的組合。此外,使用numericText(countsDown:)請(qǐng)求計(jì)時(shí)器文本的動(dòng)畫。

筆記
在包含 Always On Retina 顯示屏的設(shè)備上,系統(tǒng)不會(huì)執(zhí)行動(dòng)畫以保持 Always On 的電池壽命。確保在動(dòng)畫內(nèi)容更改之前使用 SwiftUI 的isLuminanceReduced環(huán)境值來檢測(cè) Always On。

從您的應(yīng)用程序中結(jié)束實(shí)時(shí)活動(dòng)

始終在關(guān)聯(lián)的任務(wù)或?qū)崟r(shí)事件結(jié)束后結(jié)束實(shí)時(shí)活動(dòng)。已結(jié)束的實(shí)時(shí)活動(dòng)將保留在鎖定屏幕上,直到用戶將其刪除或系統(tǒng)自動(dòng)將其刪除。自動(dòng)刪除取決于您為函數(shù)end(using:dismissalPolicy:)提供的解雇策略。此外,始終包含更新的Activity.ContentState以確保實(shí)時(shí)活動(dòng)在結(jié)束后顯示最新和最終的內(nèi)容更新。這很重要,因?yàn)閷?shí)時(shí)活動(dòng)可以在鎖定屏幕上保持可見一段時(shí)間。

以下示例顯示了披薩外賣應(yīng)用程序如何結(jié)束一個(gè)實(shí)時(shí)活動(dòng),該活動(dòng)顯示比薩送達(dá)時(shí)訂單的交付狀態(tài):

let finalDeliveryStatus = PizzaDeliveryAttributes.PizzaDeliveryStatus(driverName: "Anne Johnson", deliveryTimer: Date.now...Date())

Task {           
  await deliveryActivity?.end(using:finalDeliveryStatus, dismissalPolicy: .default)
}

上面的示例使用default解雇政策。因此,實(shí)時(shí)活動(dòng)結(jié)束后會(huì)在鎖定屏幕上顯示一段時(shí)間,以允許用戶瀏覽他們的手機(jī)以查看最新信息。用戶可以隨時(shí)選擇移除 Live Activity,或者系統(tǒng)在活動(dòng)結(jié)束四小時(shí)后自動(dòng)移除。

要立即刪除從鎖定屏幕結(jié)束的實(shí)時(shí)活動(dòng),請(qǐng)使用immediate。或者,用于after(_:)`指定四小時(shí)窗口內(nèi)的日期。雖然您可以提供任何日期,但系統(tǒng)會(huì)在給定日期之后或在實(shí)時(shí)活動(dòng)結(jié)束后四個(gè)小時(shí)后刪除已結(jié)束的實(shí)時(shí)活動(dòng) - 以先到者為準(zhǔn)。

用戶可以隨時(shí)從鎖定屏幕中刪除您的實(shí)時(shí)活動(dòng)。這會(huì)結(jié)束您的 Live Activity,但不會(huì)結(jié)束或取消用戶啟動(dòng) Live Activity 的操作。例如,用戶可以從鎖定屏幕中刪除他們的披薩外賣的實(shí)時(shí)活動(dòng),但這不會(huì)取消披薩訂單。當(dāng)用戶或系統(tǒng)刪除實(shí)時(shí)活動(dòng)時(shí)ActivityState更改為ActivityState.dismissed。

使用遠(yuǎn)程推送通知更新或結(jié)束您的實(shí)時(shí)活動(dòng)

除了使用 ActivityKit 從您的應(yīng)用程序更新和結(jié)束實(shí)時(shí)活動(dòng)之外,您還可以使用從服務(wù)器發(fā)送到 Apple 推送通知服務(wù) (APN) 的遠(yuǎn)程推送通知更新或結(jié)束實(shí)時(shí)活動(dòng)。要了解有關(guān)使用遠(yuǎn)程推送通知更新您的實(shí)時(shí)活動(dòng)的更多信息,請(qǐng)參閱使用遠(yuǎn)程推送通知更新和結(jié)束您的實(shí)時(shí)活動(dòng)

跟蹤更新

當(dāng)您啟動(dòng) Live Activity 時(shí),ActivityKit 會(huì)返回一個(gè)Activity對(duì)象。除了唯一標(biāo)識(shí)每個(gè)活動(dòng)的id之外,Activity還提供觀察內(nèi)容狀態(tài)、活動(dòng)狀態(tài)和推送令牌更新的序列。使用相應(yīng)的序列在您的應(yīng)用中接收更新,使您的應(yīng)用和 Live Activity 保持同步,并響應(yīng)更改的數(shù)據(jù):

  • 要觀察正在進(jìn)行的 Live Activity 的狀態(tài)——例如,確定它是處于活動(dòng)狀態(tài)還是已經(jīng)結(jié)束——使用activityStateUpdates

  • 要觀察 Live Activity 動(dòng)態(tài)內(nèi)容的變化,請(qǐng)使用contentState

  • 要觀察 Live Activity 的推送令牌的變化,請(qǐng)使用pushTokenUpdates

獲取活動(dòng)列表

您的應(yīng)用可以啟動(dòng)多個(gè) Live Activity。例如,體育應(yīng)用程序可能允許用戶為他們感興趣的每個(gè)現(xiàn)場(chǎng)體育比賽啟動(dòng)Live Activity。如果您的應(yīng)用程序啟動(dòng)多個(gè)Live Activity,請(qǐng)使用activityUpdates函數(shù)獲取有關(guān)您的應(yīng)用程序正在進(jìn)行的Live Activity的通知。跟蹤正在進(jìn)行的 Live Activity 以確保您的應(yīng)用程序的數(shù)據(jù)與 ActivityKit 跟蹤的活動(dòng) Live Activity 同步。

以下代碼段顯示了披薩外賣應(yīng)用程序如何檢索正在進(jìn)行的活動(dòng)列表:

// Fetch all ongoing pizza delivery Live Activities.
for await activity in Activity<PizzaDeliveryAttributes>.activityUpdates {
    print("Pizza delivery details: \(activity.attributes)")
}

獲取所有活動(dòng)的另一個(gè)用例是維護(hù)正在進(jìn)行的實(shí)時(shí)活動(dòng),并確保您不會(huì)讓任何活動(dòng)持續(xù)運(yùn)行超過需要的時(shí)間。例如,系統(tǒng)可能會(huì)停止您的應(yīng)用程序,或者您的應(yīng)用程序可能會(huì)在 Live Activity 處于活動(dòng)狀態(tài)時(shí)崩潰。當(dāng)應(yīng)用下次啟動(dòng)時(shí),檢查是否有任何活動(dòng)仍處于活動(dòng)狀態(tài),更新應(yīng)用存儲(chǔ)的 Live Activity 數(shù)據(jù),并結(jié)束任何不再相關(guān)的 Live Activity。

也可以看看

現(xiàn)場(chǎng)活動(dòng)實(shí)施

使用遠(yuǎn)程推送通知更新和結(jié)束您的實(shí)時(shí)活動(dòng)
當(dāng)您開始實(shí)時(shí)活動(dòng)時(shí)接收推送令牌,并使用它通過遠(yuǎn)程推送通知更新或結(jié)束您的實(shí)時(shí)活動(dòng)。
class Activity 用于啟動(dòng)、更新和結(jié)束實(shí)時(shí)活動(dòng)的對(duì)象。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,208評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,477評(píng)論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,960評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,200評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,726評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,617評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,807評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,049評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評(píng)論 1 281
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,432評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,769評(píng)論 2 372