Make the countdown narrow enough for the slot it sits in

Text(style: .timer) renders 1:23:45 once more than an hour out. An
accessoryCorner has room for three or four characters beside its curved label,
and a circular gauge for about the same, so the live timer overlapped what it
sat next to.

Replaced with a compact value — "23m", "2h", "3d" — recomputed per timeline
entry, which is why the timeline now carries a mark every minute for the first
hour. Both corner kinds and the gauge also get lineLimit(1) and a minimum scale
factor, so an unexpectedly wide value shrinks instead of colliding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-11 18:33:38 +02:00
co-authored by Claude Opus 5
parent fdae680e02
commit b489f10c28
4 changed files with 31 additions and 3 deletions
@@ -14,7 +14,12 @@ struct CornerCountdownComplicationView: View {
let next = WatchComplicationSupport.nextEvent(in: entry.snapshot, at: entry.date) let next = WatchComplicationSupport.nextEvent(in: entry.snapshot, at: entry.date)
Group { Group {
if let next { if let next {
Text(WatchComplicationSupport.countdownTarget(next, at: entry.date), style: .timer) Text(WatchComplicationSupport.compactCountdown(
to: WatchComplicationSupport.countdownTarget(next, at: entry.date),
at: entry.date,
language: entry.language))
.lineLimit(1)
.minimumScaleFactor(0.6)
.widgetAccentable() .widgetAccentable()
} else { } else {
Image(systemName: "calendar") Image(systemName: "calendar")
@@ -37,6 +42,8 @@ struct CornerTimeComplicationView: View {
Text(next.isAllDay Text(next.isAllDay
? WatchL10n.t("watch.allday", entry.language) ? WatchL10n.t("watch.allday", entry.language)
: WatchComplicationSupport.time(next.start, language: entry.language)) : WatchComplicationSupport.time(next.start, language: entry.language))
.lineLimit(1)
.minimumScaleFactor(0.6)
.widgetAccentable() .widgetAccentable()
} else { } else {
Image(systemName: "calendar") Image(systemName: "calendar")
@@ -24,8 +24,11 @@ struct CountdownComplicationView: View {
Gauge(value: WatchComplicationSupport.gaugeFraction(to: target, at: entry.date)) { Gauge(value: WatchComplicationSupport.gaugeFraction(to: target, at: entry.date)) {
Image(systemName: "calendar") Image(systemName: "calendar")
} currentValueLabel: { } currentValueLabel: {
Text(target, style: .timer) Text(WatchComplicationSupport.compactCountdown(to: target, at: entry.date,
.font(.system(size: 12)) language: entry.language))
.font(.system(size: 13))
.lineLimit(1)
.minimumScaleFactor(0.6)
} }
.gaugeStyle(.accessoryCircularCapacity) .gaugeStyle(.accessoryCircularCapacity)
.widgetAccentable() .widgetAccentable()
@@ -28,6 +28,22 @@ enum WatchComplicationSupport {
event.start > now ? event.start : event.end event.start > now ? event.start : event.end
} }
/// A countdown narrow enough for a corner or a circular gauge: at most
/// three characters. `Text(style: .timer)` would be live and free of
/// timeline entries, but renders `1:23:45` and overlaps whatever sits
/// beside it — so the timeline carries minute-granular entries instead and
/// this is recomputed per entry.
static func compactCountdown(to target: Date, at now: Date, language: String) -> String {
let seconds = target.timeIntervalSince(now)
guard seconds > 0 else { return WatchL10n.t("watch.now", language) }
let minutes = Int(seconds / 60)
if minutes < 1 { return WatchL10n.t("watch.now", language) }
if minutes < 60 { return "\(minutes)m" }
let hours = minutes / 60
if hours < 24 { return "\(hours)h" }
return "\(hours / 24)d"
}
/// How full a draining ring should be, over a two-hour lead window. /// How full a draining ring should be, over a two-hour lead window.
static func gaugeFraction(to target: Date, at now: Date) -> Double { static func gaugeFraction(to target: Date, at now: Date) -> Double {
let window: TimeInterval = 2 * 3600 let window: TimeInterval = 2 * 3600
+2
View File
@@ -26,6 +26,7 @@ enum WatchL10n {
"watch.calendars": "Kalender", "watch.calendars": "Kalender",
"watch.allday": "Ganztägig", "watch.allday": "Ganztägig",
"watch.no_events": "Keine Termine", "watch.no_events": "Keine Termine",
"watch.now": "jetzt",
"watch.today": "Heute", "watch.today": "Heute",
"watch.tomorrow": "Morgen", "watch.tomorrow": "Morgen",
"watch.events_today": "Termine", "watch.events_today": "Termine",
@@ -41,6 +42,7 @@ enum WatchL10n {
"watch.calendars": "Calendars", "watch.calendars": "Calendars",
"watch.allday": "All-day", "watch.allday": "All-day",
"watch.no_events": "No events", "watch.no_events": "No events",
"watch.now": "now",
"watch.today": "Today", "watch.today": "Today",
"watch.tomorrow": "Tomorrow", "watch.tomorrow": "Tomorrow",
"watch.events_today": "events", "watch.events_today": "events",