配景
2022.9.8苹果发布会上,最引人注目的一个功能灵动岛问世,固然整个发布会也只有这一个功能能拿出来提一嘴。对于用户而言灵动岛是一种新的交互式,刘海屏改成了药片屏。对于开辟者而言,我们须要研究一下能为我们的APP做些什么。
灵动岛是什么
- 灵动岛是iphone14Pro的专属特性,是iphone14pro和4 pro max两个产物的交互式。
- 在这两个系列中,把刘海屏改为药片屏幕,给了传统的打孔屏幕一个新的观念。
- 提到灵动岛,就必须知道实时活动Live Activity,实时活动分为两部门,一部门是锁屏界面的展示,一部门是灵动岛展示。假如开辟灵动岛就须要同时分身锁屏的展示。在锁定屏幕上,Live Activity体现在屏幕底部。(由于灵动岛属于LiveActivity的一部门,后边我们同一利用Live Activity)
灵动岛与Widget的关系
开辟Live Activity的代码都在Widget的Target里,利用SwiftUI和WidgetKit来创建Live Activity的用户界面。以是Live Activity的开辟跟开辟Widget差不多。
与Widget差别的是,Live Activity利用差别的机制来继承更新。Live Activity倒霉用时间线机制,利用ActivityKit大概通过远程推送关照更新。
Live Activity的要求和限定
- 除非应用步调大概用户竣事,否则Live Activity最多可以保活8小时,高出时间,体系会自动竣事。体系会将其从灵动岛中移除。但是Live Activity会不停生存在锁屏上,知道用户删除大概体系4小时后删除,以是Live Activity在锁屏上最多能存活12小时。
- 每个Live Activity在自己应用的沙盒中运行,无法访问网络和位置更新,要更新数据,须要利用ActivityKit大概利用远程推送。动态数据的巨细不能高出4Kb
- Live Activity针对锁屏和灵动岛提供差别的视图。锁屏会出现全部装备上。支持灵动岛的装备具有四种视图:紧凑前视图、紧凑尾视图、最小视图和扩展视图。
- 为确保体系可以在每个位置体现您的 Live Activity,开辟时间必须支持全部视图。
- 一个应用可以启动多个 Live Activity,而一个装备可以从多个应用运行 Live Activity,启动大概会失败,LiveActivity有上限个数
Live Activity的开辟
Live Activity的界面开辟是Widget的一部门,假如应用已经支持了Widget,就可以将代码写到Widget里。
- 创建Widget Target
- 打开Info.plist文件,添加 Supports Live Activity ,设置为YES
- 创建ActivityAttributes 布局,描述Live Activity的静态和动态数据。
- 创建ActivityConfiguration通过ActivityAttributes,启动Live Activity
- 添加代码以配置、启动、更新和竣事。
创建Live Activity
import SwiftUIimport WidgetKit@mainstruct 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. // ... } }}LiveActivity是一个Widget,以是代码是写在Widget的target里边。可以看到灵动岛的主函数就是继续与Wdiget。
上边函数的ActivityConfiguration第一个block就是锁屏界面的view。第二个block为灵动岛view。
创建锁屏界面的视图
体系要求高度不能高出160,高出的话会截掉
@mainstruct 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<izzaDeliveryAttributes> 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) }}创建灵动岛视图
灵动岛视图分为两大部门
- 一部门是灵动岛默认状态,就是紧凑状态
- 一部门是长按灵动岛的扩展状态
创建紧凑和最小的视图
默认情况下,灵动岛中的紧凑和最小视图利用玄色配景颜色和白色文本。可以利用修改keylineTint(_
import SwiftUIimport WidgetKit@mainstruct 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) } }}创建扩展视图
视图最高160,多余截断
扩展视图分为4部门
center将内容放置在原深感摄像头下方。
leading将内容沿睁开的 Live Activity 的前沿放置在原深感摄像头旁边,并在其下方包裹其他内容。
trailing将内容放置在 TrueDepth 摄像头旁边睁开的 Live Activity 的后沿,并在其下方包裹其他内容。
bottom将内容置于前导、尾随和居中内容之下。
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) } }}利用自界说颜色
体系默认设置的颜色最适实用户。假如要设置自界说颜色,可以利用activityBackgroundTint(_activitySystemActionForegroundColor(_
设置半透明,利用opacity(_
点击处置惩罚
跟Widget雷同。
锁屏、紧凑视图利用widgetURL(_跳转,全局相应。
扩展视图,利用Link,可以单独按钮相应。
启动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...futurelet 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).")}只能通过应用步调在前台时间启动,可以在背景运行时更新大概竣事,好比利用Background Tasks。
利用远程推送更新和关闭LiveActivity Updating and ending your Live Activity with remote push notifications.
更新Live Activity
应用步调处于前台或背景时,利用此API更新实时活动。还可以利用该功能在 iPhone 和 Apple Watch 上体现警报,告知人们新的 Live Activity 内容
更新数据的巨细不能高出 4KB。
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...futurelet 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)在 Apple Watch 上,体系将title和body属性用于警报。在 iPhone 上,体系不会体现常规警报,而是体现动态岛中睁开的实时活动。在不支持动态岛的装备上,体系会在主屏幕上体现一个横幅,该横幅利用您的实时活动的扩展视图。
竣事Live Activity
已竣事的实时活动将生存在锁定屏幕上,直到用户将其删除或体系自动将其删除。自动删除的控制取决于函数的dismissalPolicy参数。
别的,实时竣事了也要包含更新,包管在Live Activity竣过后体现最新和最终的结果。
let finalDeliveryStatus = PizzaDeliveryAttributes.PizzaDeliveryStatus(driverName: "Anne Johnson", deliveryTimer: Date.now...Date())Task { await deliveryActivity?.end(using:finalDeliveryStatus, dismissalPolicy: .default)}实例体现的是default战略,竣过后会生存在锁定屏幕上一段时间。用户可以选择移除,大概4个小时间体系自动移除。
要立刻从锁屏上移除的话,须要利用immedia属性,大概after指定时间。时间须要指定在4小时之内,高出4小时,体系会自动删除。
移除live activity,只会影响活动的展示,并不会对业务上产生影响。
通过推送来更新和竣事Live Activity
{ "aps": { "timestamp": 1168364460, "event": "update", "content-state": { "driverName": "Anne Johnson", "estimatedDeliveryTime": 1659416400 }, "alert": { "title": "Delivery Update", "body": "Your pizza order will arrive soon.", "sound": "example.aiff" } }}跟踪更新
您启动 Live Activity 时,ActivityKit 会返回一个Activity对象。除了id唯一标识每个活动之外,Activity还提供观察内容状态、活动状态和推送令牌更新的序列。利用相应的序列在您的应用中接收更新,使您的应用和 Live Activity 保持同步,并相应更改的数据:
- 要观察正在举行的 Live Activity 的状态——比方,确定它是处于活动状态还是已经竣事——利用.activityStateUpdates
- 要观察 Live Activity 动态内容的厘革,请利用.contentState
- 要观察 Live Activity 的推送令牌的厘革,请利用.pushTokenUpdates
获取活动列表
您的应用可以启动多个 Live Activity。比方,体育应用步调大概允许用户为他们感爱好的每个现场体育角逐启动现场活动。假如启动多个现场活动,请利用该功能获取有关您的应用步调正在举行的现场活动的关照。跟踪正在举行的 Live Activity 以确保您的应用步调的数据与 ActivityKit 跟踪的活动 Live Activity 同步。activityUpdates
以下代码段体现了披萨外卖应用步调怎样检索正在举行的活动列表:
// Fetch all ongoing pizza delivery Live Activities.for await activity in Activity<izzaDeliveryAttributes>.activityUpdates { print("izza delivery details: \(activity.attributes)")}获取全部活动的另一个用例是维护正在举行的实时活动,并确保您不会让任何活动一连运行高出须要的时间。比方,体系大概会制止您的应用步调,大概您的应用步调大概会在 Live Activity 处于活动状态时瓦解。当应用下次启动时,查抄是否有任何活动仍处于活动状态,更新应用存储的 Live Activity 数据,并竣事任何不再干系的 Live Activity。
参考文档:
官方文档 |