WidgetKit — 计时器完整示例
This article is extracted from the chat log with AI. Please identify it with caution.

我们现在就来构建这个可配置的计时器 + Live Activity + Widget 项目的完整基础工程结构。这个工程将包括:


🚀 项目名称示例(可自定义) #

FocusTimerApp


📦 目录结构概览 #

FocusTimerApp/
├── App/
│   ├── FocusTimerApp.swift            // App 入口
│   └── TimerManager.swift             // 倒计时控制器
├── Model/
│   ├── TimerType.swift                // 枚举类型(番茄、短休息等)
│   ├── TaskRecord.swift               // 任务记录结构
│   └── TimerAttributes.swift          // 用于 Live Activity 的状态结构
├── LiveActivity/
│   └── LiveActivityView.swift         // 锁屏/灵动岛视图
├── WidgetExtension/
│   ├── TaskWidget.swift               // 展示任务记录的 Widget
│   ├── ConfigurableWidget.swift       // 可配置的启动计时器 Widget
│   ├── StartTimerIntent.swift         // AppIntent:点击 Widget 启动计时器
│   └── TimerTypeEntity.swift          // AppEntity:让 Widget 支持计时器类型选择
├── Storage/
│   └── TaskStore.swift                // 任务记录存储,支持 iCloud

关键代码概览 #

TimerType.swift:定义计时器类型 #

enum TimerType: String, CaseIterable, Codable {
    case focus = "专注"
    case shortBreak = "短休息"
    case longBreak = "长休息"

    var duration: TimeInterval {
        switch self {
        case .focus: return 25 * 60
        case .shortBreak: return 5 * 60
        case .longBreak: return 15 * 60
        }
    }
}

TimerManager.swift:倒计时控制逻辑(可与 ViewModel 联动) #

class TimerManager: ObservableObject {
    @Published var timeRemaining: TimeInterval = 0
    @Published var isRunning = false
    var timer: Timer?

    func start(type: TimerType) {
        timeRemaining = type.duration
        isRunning = true
        startTimer()

        // 触发 Live Activity
        LiveActivityService.startLiveActivity(for: type)
    }

    private func startTimer() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
            self.timeRemaining -= 1
            if self.timeRemaining <= 0 {
                self.stop()
            }
        }
    }

    func stop() {
        isRunning = false
        timer?.invalidate()
        timer = nil

        LiveActivityService.endLiveActivity()
    }
}

TimerAttributes.swift(用于 Live Activity) #

import ActivityKit

struct TimerAttributes: ActivityAttributes {
    struct ContentState: Codable, Hashable {
        var timeRemaining: TimeInterval
    }

    var type: TimerType
}

LiveActivityView.swift #

struct LiveActivityView: View {
    let context: ActivityViewContext<TimerAttributes>

    var body: some View {
        VStack {
            Text("计时中:\(context.attributes.type.rawValue)")
            Text("\(Int(context.state.timeRemaining)) 秒")
                .monospacedDigit()
        }
        .padding()
    }
}

StartTimerIntent.swift(Widget 可点击交互按钮) #

struct StartTimerIntent: AppIntent {
    static var title: LocalizedStringResource = "开始计时"

    @Parameter(title: "计时器类型")
    var type: TimerTypeEntity

    func perform() async throws -> some IntentResult {
        TimerManager().start(type: type.toTimerType())
        return .result()
    }
}

TaskWidget.swift(展示最近任务记录) #

struct TaskWidget: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(kind: "TaskWidget", provider: TaskTimelineProvider()) { entry in
            VStack(alignment: .leading) {
                Text("最近任务")
                ForEach(entry.tasks.prefix(3), id: \.id) {
                    Text("• \($0.type.rawValue)")
                }
            }
            .padding()
        }
        .configurationDisplayName("任务记录")
        .description("展示最近完成的计时任务")
    }
}

ConfigurableWidget.swift(点击即可启动计时器) #

struct ConfigurableWidget: Widget {
    var body: some WidgetConfiguration {
        IntentConfiguration(
            kind: "ConfigurableTimerWidget",
            intent: StartTimerIntent.self,
            provider: EmptyTimelineProvider()
        ) { _ in
            Button(intent: StartTimerIntent(type: .init(timerType: .focus))) {
                Text("开始专注")
            }
        }
        .configurationDisplayName("开始计时")
        .description("点击启动指定类型的计时器")
        .supportedFamilies([.systemSmall])
    }
}

☁️ TaskStore.swift(保存计时记录,可接入 iCloud) #

final class TaskStore {
    static let shared = TaskStore()

    @Published var records: [TaskRecord] = []

    func addRecord(for type: TimerType) {
        let record = TaskRecord(type: type, date: Date())
        records.insert(record, at: 0)
        save()
    }

    private func save() {
        let data = try? JSONEncoder().encode(records)
        UserDefaults.standard.set(data, forKey: "TaskRecords")
    }

    private func load() {
        if let data = UserDefaults.standard.data(forKey: "TaskRecords"),
           let decoded = try? JSONDecoder().decode([TaskRecord].self, from: data) {
            records = decoded
        }
    }
}
本文共 1161 字,创建于 May 21, 2025
相关标签: Xcode, ByAI, ChatGPT, SwiftUI