文章目录
灵动岛(Dynamic Island)和实时活动(Live Activities)是 iPhone 为了便于用户在退出APP时,仍然能随时关注进行中的活动、高频操作的组件。其中:
- 灵动岛是,当用户退出当前App后,仍然在手机前置镜头附近展示的信息/状态,有小岛(minimal )大岛(compact )两种展示状态。
- 实时活动是,在锁屏界面保持实时更新的信息卡片,其展示优先级高于通知。
先新建一个实时活动组件
-
新建APP项目
新建一个
Swift APP
项目,在 Info.plist 中加入一个新的条目Supports Live Activities
(或者使用原始Key:NSSupportsLiveActivities
), 并且设置为YES
, 告诉系统你的 App 需要使用 Live Activity 的权限。 否则调用 API 的时候会直接报错(最新版本的 Xcode 才能找到这个选项)。 -
创建实时活动组件项目。
然后创建一个实时活动组件、按照
File -> New -> Target
打开标签卡,搜索Widget Extension
。
-
创建后的目录结构如下
-
将
LiveActivityWidgetLiveActivit.swift
文件的Target Membership 勾选上我们的 App 项目,这样做 App 代码中就可以引用到 Widget 中的内容 。
-
现在就可以运行起来了。(但是此时可能你的灵动岛没任何反应,锁屏的实时界面没任何内容,这个是正常的,因为还没有增加启动灵动岛的代码呢,)
熟悉下实时活动的代码
- 在
LiveActivityWidgetLiveActivit.swift
文件中,定义了两个结构体WidgetAttributes
和WidgetLiveActivity
。WidgetAttributes
定义的是Dynamic IsIand的数据结构,WidgetLiveActivity
定义了组件的View。
Swift
struct LiveActivityWidgetAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
// Dynamic stateful properties about your activity go here!
var emoji: String
}
// Fixed non-changing properties about your activity go here!
var name: String
}
struct LiveActivityWidgetLiveActivity: Widget {
}
- 启动
Live Activity
,
我们需要一个位置,启动实时活动组件,在 App 主项目中,引入 ActivityKit ,然后调用它的Activity.request
方法:
Swift
//
// ViewController.swift
// LiveActivity
//
// Created by yu tian on 2024/7/31.
//
import UIKit
import ActivityKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let state = LiveActivityWidgetAttributes.ContentState(emoji: "😀");
let attr = LiveActivityWidgetAttributes(name: "hello")
do {
try Activity.request(attributes: attr, contentState: state);
}catch {
print("err\(error)")
}
}
}
现在编译代码,就可以运行我们的组件了。
UI代码解析
灵动岛的UI结构如下:
灵动岛的UI代码均在WidgetLiveActivity
结构中,想要实现功能,在对应的View中修改即可。
Swift
// 配置UI,共计需要设计出 4 个 UI,且全都需要实现
struct LiveActivityWidgetLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: LiveActivityWidgetAttributes.self) { context in
// 锁屏UI,出现在所有设备上 - 第一个UI
// 不支持灵动岛的未锁屏的设备上,显示为 banner UI
// 两个都使用同一个View组件,在这里配置
// 系统使用默认的文本颜色和最适合锁定屏幕的实时活动背景色
VStack {
Text("Your \(context.state.driverName) is on the way!")
}
.activityBackgroundTint(Color.cyan) // 修改背景颜色
.activitySystemActionForegroundColor(Color.black) // 修改文本颜色
} dynamicIsland: { context in
// 灵动岛UI
DynamicIsland {
// 展开后的UI - 第二个UI
// 需要组合不同的区域
DynamicIslandExpandedRegion(.leading) {
// 展开后的前面
Text("Leading")
}
DynamicIslandExpandedRegion(.trailing) {
// 展开后的后面
Text("Trailing")
}
DynamicIslandExpandedRegion(.center) {
// 展开后的中间
Text("Center")
}
DynamicIslandExpandedRegion(.bottom) {
// 展开后的底部
Text("Bottom")
}
} compactLeading: {
// 紧凑样式的左边 - 第三个UI(左)
Text("L")
} compactTrailing: {
// 紧凑样式的右边 - 第三个UI(右)
Text("T")
} minimal: {
// 当有多个 Live Activity时,灵动岛显示成circular minimal 样式 - 第四个UI
Text("Min")
}
.widgetURL(URL(string: "tianyiyi://ViewController")) // 点击跳转到指定页面
.keylineTint(Color.red)
}
}
}
待完善: