Initial commit: EzTimer iOS app with widget extension
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
107
EzTimerWidget/EzTimerWidgetLiveActivity.swift
Normal file
107
EzTimerWidget/EzTimerWidgetLiveActivity.swift
Normal file
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// EzTimerWidgetLiveActivity.swift
|
||||
// EzTimerWidget
|
||||
//
|
||||
// Created by Jared Evans on 12/17/25.
|
||||
//
|
||||
|
||||
import ActivityKit
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
struct EzTimerWidgetLiveActivity: Widget {
|
||||
var body: some WidgetConfiguration {
|
||||
ActivityConfiguration(for: TimerAttributes.self) { context in
|
||||
// Lock Screen/Banner UI
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Image(systemName: "timer")
|
||||
.foregroundColor(.red)
|
||||
Text("VizTimer")
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
HStack {
|
||||
// This counts down automatically relative to the target date
|
||||
if context.state.isFinished {
|
||||
Text("Done at \(context.state.endTime, style: .time)")
|
||||
.font(.system(size: 40, weight: .bold)) // Slightly smaller to fit time
|
||||
.foregroundColor(.white)
|
||||
} else {
|
||||
Text(timerInterval: Date()...context.state.endTime, countsDown: true)
|
||||
.font(.system(size: 48, weight: .bold))
|
||||
.monospacedDigit()
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
if !context.state.message.isEmpty {
|
||||
Text(context.state.message)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.white)
|
||||
.multilineTextAlignment(.trailing)
|
||||
.lineLimit(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.activityBackgroundTint(Color.black.opacity(0.8))
|
||||
.activitySystemActionForegroundColor(Color.red)
|
||||
|
||||
} dynamicIsland: { context in
|
||||
DynamicIsland {
|
||||
// Expanded State
|
||||
DynamicIslandExpandedRegion(.leading) {
|
||||
Image(systemName: "timer")
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
DynamicIslandExpandedRegion(.trailing) {
|
||||
if context.state.isFinished {
|
||||
Text("Done at \(context.state.endTime, style: .time)")
|
||||
.font(.headline)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Text(timerInterval: Date()...context.state.endTime, countsDown: true)
|
||||
.monospacedDigit()
|
||||
.font(.title2)
|
||||
}
|
||||
}
|
||||
DynamicIslandExpandedRegion(.bottom) {
|
||||
if !context.state.message.isEmpty {
|
||||
Text(context.state.message)
|
||||
.font(.caption)
|
||||
.foregroundColor(.white)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
.padding(.top, 4)
|
||||
} else {
|
||||
Text("Time Remaining")
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
} compactLeading: {
|
||||
Image(systemName: "timer")
|
||||
.foregroundColor(.red)
|
||||
} compactTrailing: {
|
||||
if context.state.isFinished {
|
||||
Text("End")
|
||||
.fontWeight(.bold)
|
||||
.frame(minWidth: 40)
|
||||
} else {
|
||||
Text(timerInterval: Date()...context.state.endTime, countsDown: true)
|
||||
.monospacedDigit()
|
||||
.frame(minWidth: 40)
|
||||
}
|
||||
} minimal: {
|
||||
Image(systemName: "timer")
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
.widgetURL(URL(string: "viztimer://open"))
|
||||
.keylineTint(Color.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user