前言
- 灵动岛是从iPhone14 Pro开始出现的,充分利用顶部刘海的一种UI展示
- 环境的话,需要至少Xcode14.1以及iOS16.1系统的手机或者模拟器
- 灵动岛是Widgets系统的一部分,需要你的App有widget extension或者新建一个Widget Extension的Target
3. 同时需要你的App支持Live Activity,这需要在你主工程的Info.plist中添加一个key(NSSupportsLiveActivities),类型为bool,需要设置为YES
开始
iOS16.1开始,苹果提供了自定义灵动岛的API,它位于WidgetKit动态库中,通过提供给我们一个叫做 *ActivityConfiguration*的configuration来允许我们定义一个 live activity widget。
- 首先我们需要先定义一个 ActivityAttributes供Widget使用
struct FastingAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
// 动态数据
// Dynamic stateful properties about your activity go here!
var value: Int
}
// Fixed non-changing properties about your activity go here!
// 静态不变的数据
var name: String
}
- 创建Activity所需要的SwiftUIView,其中context的 attributes 就是上面我们定义的 FastingAttributes
@available(iOSApplicationExtension 16.1, *)
struct FastingActivityWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: FastingAttributes.self) { context in
LiveActivityView(context: context)
.padding(.horizontal)
} dynamicIsland: { context in
DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
LiveActivityView(context: context)
}
} compactLeading: {
Image(systemName: "circle")
.foregroundColor(.green)
} compactTrailing: {
Text(verbatim: context.state.progress.formatted(.percent.precision(.fractionLength(0))))
.foregroundColor(context.state.stage?.color)
} minimal: {
Image(systemName: "circle")
.foregroundColor(.green)
}
}
}
}
- 我们在这里使用 ActivityConfiguration 来创建了一个Live Activity Widget,在构造函数里使用 灵动岛字段dynamicIsland 来配置灵动岛UI。
- dynamicIsland 的构造函数有四个参数,需要我们提供
compact leading,compact trailing,expanded和minimal四种情况下所对应的SwiftUI View。第一个闭包对应的是expanded,其余几个分别对应不同的参数
- 通过ActivityKit的API启动这个Activity,并且当我们退出App时会有个灵动岛的动画
import ActivityKit
let attributes = FastingAttributes(name: "unravel")
do {
_ = try Activity<FastingAttributes>.request(attributes: attributes, contentState: .init(value: 3))
} catch {}
4. 可以通过以下API停掉所有的FastingAttributes的Live Activity,或者通过上一步request返回的activity进行停掉
Task {
for activity in Activity<FastingAttributes>.activities {
await activity.end(dismissalPolicy: .immediate)
}
}
展示规则
- 无论何时,只要你的App是那一刻运行live activity widget 的唯一应用,系统就会相应的展示
compact leading和compact trailing的Views
2. 如果那一刻运行live activity widget的应用不止一个,系统就会展示 minimal view并且将它展示为 单独的trailing view
- 当用户在灵动岛上长按时,就会展示 expanded view
这种情况下,灵动岛的空间分为如上的四部分,系统允许我们控制每一个部分的内容并且提供了priority参数去优先展示
@available(iOSApplicationExtension 16.1, *)
struct FastingActivityWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: FastingAttributes.self) { context in
LiveActivityView(context: context)
} dynamicIsland: { context in
// Expanded下的灵动岛
DynamicIsland {
// 分别控制不同部分的内容,使用优先级优先展示
DynamicIslandExpandedRegion(.leading, priority: 1) {
LiveActivityView(context: context)
}
DynamicIslandExpandedRegion(.trailing) {
LiveActivityView(context: context)
}
DynamicIslandExpandedRegion(.center) {
LiveActivityView(context: context)
}
DynamicIslandExpandedRegion(.bottom) {
LiveActivityView(context: context)
}
} compactLeading: {
Image(systemName: "circle")
.foregroundColor(.green)
} compactTrailing: {
Text(verbatim: context.state.progress.formatted(.percent.precision(.fractionLength(0))))
.foregroundColor(context.state.stage?.color)
} minimal: {
Image(systemName: "circle")
.foregroundColor(.green)
}
}
}
}
如果我们的内容太宽的话,系统还提供了dynamicIsland 视图修改器将leadingview放置到摄像头下方
@available(iOSApplicationExtension 16.1, *)
struct FastingActivityWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: FastingAttributes.self) { context in
LiveActivityView(context: context)
} dynamicIsland: { context in
DynamicIsland {
DynamicIslandExpandedRegion(.leading, priority: 1) {
LiveActivityView(context: context)
// 灵动岛视图修改器
.dynamicIsland(verticalPlacement: .belowIfTooWide)
}
} compactLeading: {
Image(systemName: "circle")
.foregroundColor(.green)
} compactTrailing: {
Text(verbatim: context.state.progress.formatted(.percent.precision(.fractionLength(0))))
.foregroundColor(context.state.stage?.color)
} minimal: {
Image(systemName: "circle")
.foregroundColor(.green)
}
}
}
}
默认情况下系统使用 黑色 填充 compac和minimal的背景色,我们可以通过 keylineTint这个视图修改器来改变这个颜色
@available(iOSApplicationExtension 16.1, *)
struct FastingActivityWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: FastingAttributes.self) { context in
LiveActivityView(context: context)
} dynamicIsland: { context in
DynamicIsland {
DynamicIslandExpandedRegion(.leading, priority: 1) {
LiveActivityView(context: context)
.dynamicIsland(verticalPlacement: .belowIfTooWide)
}
} compactLeading: {
Image(systemName: "circle")
.foregroundColor(.green)
} compactTrailing: {
Text(verbatim: context.state.progress.formatted(.percent.precision(.fractionLength(0))))
.foregroundColor(context.state.stage?.color)
} minimal: {
Image(systemName: "circle")
.foregroundColor(.green)
}
// 修改灵动岛内compact和minimal下的背景色
.keylineTint(.white)
}
}
}