iOS16.1开始允许开发者和灵动岛交互。引入新概念Live Activity 实时活动,我们简称LA。
* LA无法访问网络、接收定位信息, 如果要更新数据,需要通过app的ActivityKit.framework 或者 接收远程推送APNs。-
数据通信的大小限制,不论是本地数据还是APNs是数据,给到LA的数据都不能超过4KB
-
启动灵动岛Live Activity可能会失败,因为设备有启动灵动岛的个数限制
-
用户不能主动设置视图动画,系统会帮你做过渡动画
-
除非App或用户结束LA,否则最多可以活跃8个小时。 超过8小时,系统自动结束。当LA结束时候,系统会立即将其从灵动岛中移除。
但是,LA会保留在锁定屏幕上,直到用户将其删除或在系统将其删除之前再保留最多四个小时——以先到者为准。 因此,实时活动会在锁定屏幕上保留最多 12 小时。
activityEnablementUpdates:用于监听LA可用状态改变
App只能在前台启动LA。 在前后台都可以更新 或 中止LA。
遵循用时打开,不用就关闭的原则。App退出后,如果LA还没退出可能导致crash。
1 Info.plist 增加 Key为NSSupportsLiveActivities, Value为YES
2 创建tareget widget
3 开始自定义交互的UI
4 在App中进行LA的启动、更新、终止
//定义模型特征
public struct MyWidgetAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
// 可变特征,可以在App内或者APNs中更新属性,系统自动刷新视图
public var prograssState: PrograssState
}
// Fixed non-changing properties about your activity go here!
// 固定特征,在初始化Live Activity时指定,后续更新视图也不再变化
public var name: String
}
@main
struct MyWidgetLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: MyWidgetAttributes.self) { context in
// 启动LiveActivity后,锁屏页 或者 通知栏页中 将展示实时活动视图
// 锁屏下,不支持灵动岛视图。但会通知栏页常驻
// 参考图1
} dynamicIsland: { context in
DynamicIsland {
// Dynamic Island Expanded view
// 灵动岛长按展开后的视图
// 参考图2
DynamicIslandExpandedRegion(.leading) {
//leading位置的视图设置
}
DynamicIslandExpandedRegion(.trailing) {
//trailing位置的视图设置
}
DynamicIslandExpandedRegion(.center) {
//center位置的视图设置
}
DynamicIslandExpandedRegion(.bottom) {
//bottom位置的视图设置
}
} compactLeading: {
// Dynamic Island compact leading view
// 灵动岛未展开时,leading位置的视图设置
// 参考图3
} compactTrailing: {
// Dynamic Island compact trailing view
// 灵动岛未展开时,trailing位置的视图设置
// 参考图3
} minimal: {
// Dynamic Island minimal view
// 当有多个Live Activity时,
// 系统将选择其中一个Live Activity作为最小化的视图展示
// 参考图4
}
}
}
}
长按灵动岛将展开如下,可以分别根据不同位置设置视图。 如果Bottom区域不设置视图,Leading、Trailing、Center可以向下占据空间。
参考图1:启动LiveActivity后,锁屏页 或者 通知栏页中 将展示实时活动视图
用户可以侧滑删除,来关闭通知中心页的Live Activity实时活动视图,同时也会关闭灵动岛上的视图
参考图2:灵动岛长按展开后的视图
参考图3:灵动岛未展开时,leading和trailing的视图设置
参考图4: 当有多个Live Activity时, 系统将选择其中一个Live Activity作为最小化的视图展示
启动实时活动
let current = try Activity.request(attributes: attri, contentState: state, pushType: .token)
//监听Activity的回调
Task {
//监听Token变化
for await tokenData in current.pushTokenUpdates {
let mytoken = tokenData.map { String(format: "%02x", $0) }.joined()
print("activity push token", mytoken)
}
}
Task {
//监听state状态变化, 状态变化:active,end,dismissed等
for await state in current.contentStateUpdates {
print("content state update: tip=\(state.prograssState)")
}
}
Task {
//监听视图的声明周期,
for await state in current.activityStateUpdates {
print("activity state update: tip=\(state) id:\(current.id)")
}
}
更新实时活动
//构造数据模型
let state = MyWidgetAttributes.ContentState(prograssState: state)
let alertConfiguration = AlertConfiguration(title: "Delivery Update ", body: "Delivery Update State to \(state.prograssState.desc())", sound: .default)
//更新实时活动视图内容,同时发起一条本地通知
await current.update(using: state, alertConfiguration: alertConfiguration)
//或者 仅更新实时活动视图内容
await current.update(using: state, alertConfiguration: nil)
app进入前台,发起LA业务,采集LA token,上传我们的服务器后进行推送。 App或用户结束LA时,token失效。
apns请求头:
apns-push-type: liveactivity
apns-topic: .push-type.liveactivity
apns请求体增加字段content-state, 会序列化到Live Activity中的state并更新视图
注意: timestamp必须是当前时间戳,否则会推送失败
apns请求体样例:
{
"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"
}
}
}
设备收到APNs通知后,灵动岛会自动展开、渲染视图、动画,然后关闭。 视频如下:
Github Demo:
https://github.com/aklee/LiveActivityDemo/edit/main/README.md