下面我会帮你从零构建一个完整的 计时器类 Live Activity 示例项目,分为两大部分:
主 App(启动、更新、结束 Live Activity)
Widget Extension(展示在锁屏 / 动态岛)
✅ 1. 创建项目与结构 #
在 Xcode 中:
创建一个 iOS App
勾选 “Include Widget Extension”
命名主 App 为:
TimerLiveActivityApp
命名 Widget Extension 为:
TimerLiveActivityExtension
🧱 2. 定义 ActivityAttributes(在主 App 中) #
新建文件:TimerAttributes.swift
import ActivityKit
struct TimerAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
var remainingTime: TimeInterval
}
var taskName: String
}
🚀 3. 启动 Live Activity(在主 App 中) #
在 ContentView.swift
中:
import SwiftUI
import ActivityKit
struct ContentView: View {
@State private var activity: Activity<TimerAttributes>?
var body: some View {
VStack(spacing: 20) {
Button("启动计时器 (25分钟)") {
startTimer()
}
.buttonStyle(.borderedProminent)
Button("更新剩余时间 (10分钟)") {
updateTimer()
}
.disabled(activity == nil)
Button("结束计时器") {
endTimer()
}
.disabled(activity == nil)
}
.padding()
}
func startTimer() {
let attributes = TimerAttributes(taskName: "专注任务")
let contentState = TimerAttributes.ContentState(remainingTime: 25 * 60)
do {
activity = try Activity<TimerAttributes>.request(
attributes: attributes,
contentState: contentState,
pushType: nil
)
} catch {
print("启动失败: \(error)")
}
}
func updateTimer() {
Task {
await activity?.update(using: TimerAttributes.ContentState(remainingTime: 10 * 60))
}
}
func endTimer() {
Task {
await activity?.end(dismissalPolicy: .immediate)
activity = nil
}
}
}
📱 4. 设计 Widget Extension #
修改 Widget 文件名(可选) #
将自动生成的 Widget 重命名为 TimerLiveActivityWidget.swift
修改内容如下: #
import ActivityKit
import WidgetKit
import SwiftUI
struct TimerLiveActivityWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: TimerAttributes.self) { context in
// 锁屏界面
VStack {
Text(context.attributes.taskName)
.font(.headline)
Text("剩余时间: \(Int(context.state.remainingTime / 60)) 分钟")
}
.padding()
} dynamicIsland: { context in
// 动态岛界面
DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
Text("🕒")
}
DynamicIslandExpandedRegion(.trailing) {
Text("\(Int(context.state.remainingTime / 60)) min")
.bold()
}
DynamicIslandExpandedRegion(.bottom) {
Text("正在专注中...")
.font(.caption)
}
} compactLeading: {
Text("⏱")
} compactTrailing: {
Text("\(Int(context.state.remainingTime / 60))")
} minimal: {
Text("⏱")
}
}
}
}
🛠 5. 配置 Extension 的 Info.plist #
确保包含下列键值:
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>com.apple.widgetkit.live-activity</key>
<true/>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</dict>
🧪 6. 真机测试 & 模拟更新 #
Live Activities 仅支持真机测试,特别是动态岛部分。你可以:
点击“启动计时器”
回到主屏幕或锁屏,观察 Live Activity 展示
点“更新剩余时间”后观察变化
点“结束计时器”后 UI 消失
📌 后续可拓展内容(可选) #
自动倒计时更新(使用 Timer)
使用
pushType: .token
获取远程更新能力提供多个计时任务并发
使用后台任务继续刷新状态(Background Tasks)