Archived
Render the circular complications as edge-to-edge closed gauges
Date and "events today" filled their slot with AccessoryWidgetBackground and centred text, which reads as a small element with space around it. Both are now Gauges in .accessoryCircularCapacity — the WidgetKit equivalent of ClockKit's ClosedGauge templates, which no longer exist and cannot be reached from a WidgetKit complication at all. The rings carry meaning rather than decorating: the date's drains through the day, and the count's drains as today's appointments are worked through. Both drain, matching the countdown rather than inventing a second direction. An empty day leaves the ring empty instead of full, so it does not read as an untouched one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b489f10c28
commit
938ca7ca98
@@ -69,17 +69,19 @@ struct DateComplicationView: View {
|
|||||||
Label("\(WatchComplicationSupport.monthAbbreviation(entry.date, language: entry.language)) \(day)",
|
Label("\(WatchComplicationSupport.monthAbbreviation(entry.date, language: entry.language)) \(day)",
|
||||||
systemImage: "calendar")
|
systemImage: "calendar")
|
||||||
default:
|
default:
|
||||||
ZStack {
|
// A closed gauge, filling the slot edge to edge — the WidgetKit
|
||||||
AccessoryWidgetBackground()
|
// equivalent of ClockKit's ClosedGaugeText, which no longer exists.
|
||||||
VStack(spacing: 0) {
|
// The ring is not decoration: it drains as the day does.
|
||||||
Text("\(day)")
|
Gauge(value: WatchComplicationSupport.dayRemainingFraction(at: entry.date)) {
|
||||||
.font(.system(size: 22, weight: .bold))
|
Text(WatchComplicationSupport.monthAbbreviation(entry.date, language: entry.language))
|
||||||
.minimumScaleFactor(0.7)
|
} currentValueLabel: {
|
||||||
.widgetAccentable()
|
Text("\(day)")
|
||||||
Text(WatchComplicationSupport.monthAbbreviation(entry.date, language: entry.language))
|
.font(.system(size: 20, weight: .semibold))
|
||||||
.font(.system(size: 8, weight: .semibold))
|
.lineLimit(1)
|
||||||
}
|
.minimumScaleFactor(0.6)
|
||||||
}
|
}
|
||||||
|
.gaugeStyle(.accessoryCircularCapacity)
|
||||||
|
.widgetAccentable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,18 +101,17 @@ struct TodayCountComplicationView: View {
|
|||||||
Label("\(count) \(WatchL10n.t("watch.events_today", entry.language))",
|
Label("\(count) \(WatchL10n.t("watch.events_today", entry.language))",
|
||||||
systemImage: "calendar")
|
systemImage: "calendar")
|
||||||
default:
|
default:
|
||||||
ZStack {
|
Gauge(value: WatchComplicationSupport.todayRemainingFraction(in: entry.snapshot,
|
||||||
AccessoryWidgetBackground()
|
at: entry.date)) {
|
||||||
VStack(spacing: 0) {
|
Image(systemName: "calendar")
|
||||||
Text("\(count)")
|
} currentValueLabel: {
|
||||||
.font(.system(size: 22, weight: .bold))
|
Text("\(count)")
|
||||||
.widgetAccentable()
|
.font(.system(size: 20, weight: .semibold))
|
||||||
Text(WatchL10n.t("watch.events_today", entry.language).uppercased())
|
.lineLimit(1)
|
||||||
.font(.system(size: 7, weight: .semibold))
|
.minimumScaleFactor(0.6)
|
||||||
.lineLimit(1)
|
|
||||||
.minimumScaleFactor(0.6)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.gaugeStyle(.accessoryCircularCapacity)
|
||||||
|
.widgetAccentable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,31 @@ enum WatchComplicationSupport {
|
|||||||
return min(1, max(0, target.timeIntervalSince(now) / window))
|
return min(1, max(0, target.timeIntervalSince(now) / window))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// How much of today is still ahead. Drains through the day, matching the
|
||||||
|
/// countdown ring rather than inventing a second direction.
|
||||||
|
static func dayRemainingFraction(at now: Date,
|
||||||
|
calendar: Calendar = Calendar(identifier: .gregorian)) -> Double {
|
||||||
|
let dayStart = calendar.startOfDay(for: now)
|
||||||
|
guard let dayEnd = calendar.date(byAdding: .day, value: 1, to: dayStart) else { return 0 }
|
||||||
|
let total = dayEnd.timeIntervalSince(dayStart)
|
||||||
|
guard total > 0 else { return 0 }
|
||||||
|
return min(1, max(0, dayEnd.timeIntervalSince(now) / total))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The share of today's appointments still ahead. Zero when there are none,
|
||||||
|
/// which leaves the ring empty rather than full — an empty day should not
|
||||||
|
/// look like an untouched one.
|
||||||
|
static func todayRemainingFraction(in snapshot: CalendarrSnapshot?, at now: Date,
|
||||||
|
calendar: Calendar = Calendar(identifier: .gregorian)) -> Double {
|
||||||
|
guard let snapshot else { return 0 }
|
||||||
|
let dayStart = calendar.startOfDay(for: now)
|
||||||
|
guard let dayEnd = calendar.date(byAdding: .day, value: 1, to: dayStart) else { return 0 }
|
||||||
|
let today = snapshot.events.filter { $0.start < dayEnd && $0.end > dayStart }
|
||||||
|
guard !today.isEmpty else { return 0 }
|
||||||
|
let ahead = today.filter { $0.end > now }.count
|
||||||
|
return min(1, max(0, Double(ahead) / Double(today.count)))
|
||||||
|
}
|
||||||
|
|
||||||
static func time(_ date: Date, language: String) -> String {
|
static func time(_ date: Date, language: String) -> String {
|
||||||
let formatter = DateFormatter()
|
let formatter = DateFormatter()
|
||||||
formatter.locale = WatchL10n.locale(language)
|
formatter.locale = WatchL10n.locale(language)
|
||||||
|
|||||||
Reference in New Issue
Block a user