约束条件
1.Live Activity
最多可以保持8小时的活动状态
2.已结束的Live Activity
,在锁定屏幕上最多保留4个小时,所以实时活动在锁屏上最多保留12小时
3.最小化演示图像不能超过45x36.67pt
4.ActivityKit
更新和 ActivityKit
推送通知的更新动态数据大小不能超过 4 KB。
5.Live Activity
的高度超过160pt,系统可能会截断该活动
灵动岛配置
1.创建Live Activity
1.在主项目中添加target
,File -> New -> Target...
2.选择Widget Extension
,勾选Include Live Activity
,会自动创建模版代码
2.添加Info.plist
配置
在主项目的Info.plist
文件中添加Supports Live Activities
配置,设置为YES
<key>NSSupportsLiveActivities</key>
<true/>
3.代码部分
主项目中
- 创建
ActivityAttributes
文件,用来提供灵动岛上展示的数据
import Foundation
import ActivityKit
struct LiveActvityWidgetAttributes: ActivityAttributes {
typealias LiveActivityWidgetState = ContentState
public struct ContentState: Codable, Hashable {
// 可变的属性需要放在这里,activity调用update进行数据的更新
var emoji: String
}
// 灵动岛的初始化数据,描述不可变的数据
var name: String
var logo: String
var subName: String
}
2.将创建的LiveActvityWidgetAttributes
共享给灵动岛扩展组件使用
3.创建灵动岛并初始化数据
-
request(attributes:content:pushType:)
请求并启动Live activity -
update(_:)
更新Live Activity的动态内容 -
end(_: dismissalPolicy:)
结束正在进行的Live activity
import UIKit
import ActivityKit
class ViewController: UIViewController {
var liveActivity: Activity<LiveActvityWidgetAttributes>? = nil
@IBOutlet weak var openButton: UIButton! {
didSet {
openButton.setTitle("开启灵动岛", for: .normal)
openButton.setTitle("关闭灵动岛", for: .selected)
}
}
@IBOutlet weak var updateButton: UIButton! {
didSet {
updateButton.setTitle("更新灵动岛", for: .normal)
updateButton.setTitle("已更新灵动岛", for: .selected)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func actvityContent(emoji: String) -> ActivityContent<LiveActvityWidgetAttributes.LiveActivityWidgetState> {
let state = LiveActvityWidgetAttributes.LiveActivityWidgetState(emoji: emoji)
let content = ActivityContent<LiveActvityWidgetAttributes.LiveActivityWidgetState>(state: state, staleDate: nil)
return content
}
@IBAction func onOpen(_ sender: Any) {
guard ActivityAuthorizationInfo().areActivitiesEnabled else {
print("不支持")
return
}
openButton.isSelected.toggle()
guard openButton.isSelected else {
// 关闭灵动岛
Task {
await liveActivity?.end(nil, dismissalPolicy:.immediate)
}
updateButton.isSelected.toggle()
return
}
do {
// 创建灵动岛并初始化数据
let attributes = LiveActvityWidgetAttributes(name: "灵动岛", logo: "apple.logo", subName: "这是长按展开显示的文案")
let content = actvityContent(emoji: "😀")
self.liveActivity = try Activity<LiveActvityWidgetAttributes>.request(attributes: attributes, content: content)
} catch {
debugPrint("打开灵动岛失败")
}
}
// 更新灵动岛数据
@IBAction func onUpdate(_ sender: Any) {
guard openButton.isSelected else { return }
updateButton.isSelected.toggle()
Task {
let content = actvityContent(emoji: "🤩")
await liveActivity?.update(content)
}
}
}
扩展组件代码中
-
context.attributes
获取静态数据 -
context.state
获取动态数据
import ActivityKit
import WidgetKit
import SwiftUI
struct LiveActvityWidgetLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: LiveActvityWidgetAttributes.self) { context in
// 小技巧:使用该方式进行日志打印
let _ = debugPrint("context: \(context.attributes)")
// 锁屏之后,显示的桌面通知栏位置,这里可以做相对复杂的布局
VStack {
Text("Hello \(context.state.emoji)")
}
.activityBackgroundTint(Color.cyan)
.activitySystemActionForegroundColor(Color.black)
} dynamicIsland: { context in
// 灵动岛布局
DynamicIsland {
/*
长按灵动岛区域展开的UI
有4个区域布局:左,右,中间(硬件下方),底部
*/
DynamicIslandExpandedRegion(.leading) {
VStack(alignment: .leading) {
Image(systemName: "\(context.attributes.logo)")
.resizable()
.frame(width: 30, height: 30)
Text("apple logo")
}
}
DynamicIslandExpandedRegion(.trailing) {
Image(systemName: "apple.terminal.on.rectangle.fill")
}
DynamicIslandExpandedRegion(.center) {
Text("\(context.attributes.name)")
}
DynamicIslandExpandedRegion(.bottom) {
VStack {
Image(systemName: "sun.max.fill")
.resizable()
.frame(width: 30, height: 30)
.foregroundColor(.yellow)
Text("\(context.attributes.subName)")
}
}
} compactLeading: {
// 未展示开边的布局
Image(systemName: "\(context.attributes.logo)")
} compactTrailing: {
// 未展示右边布局
Text("\(context.state.emoji)")
} minimal: {
// 最小型样式,当有多个任务的情况下,位置在右边的一个圆圈区域
Image(systemName: "figure.wave.circle.fill").foregroundColor(.red)
}
// 点击整个区域,通过deeplink将数据传递给主工程
.widgetURL(URL(string: "http://www.apple.com"))
.keylineTint(Color.red)
}
}
}
效果图
参考文档:
ActivityKit文档
iOS灵动岛开发实践