From 1b24e042de3909712134e51b137ed8ef6c9a409b Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Thu, 11 Dec 2025 00:23:42 +0530 Subject: [PATCH 1/2] Fix: Added scroll shadow in presets and event scroll --- .../Calendar/DynamicIslandCalendar.swift | 48 ++++++++++++------- .../components/Notch/NotchTimerView.swift | 36 +++++++++----- 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/DynamicIsland/components/Calendar/DynamicIslandCalendar.swift b/DynamicIsland/components/Calendar/DynamicIslandCalendar.swift index f5fdd95..81342ce 100644 --- a/DynamicIsland/components/Calendar/DynamicIslandCalendar.swift +++ b/DynamicIsland/components/Calendar/DynamicIslandCalendar.swift @@ -296,27 +296,41 @@ struct EventListView: View { var body: some View { ScrollViewReader { proxy in - List { - ForEach(filteredEvents) { event in - Button(action: { - if let url = event.calendarAppURL() { - openURL(url) + ZStack { + List { + ForEach(filteredEvents) { event in + Button(action: { + if let url = event.calendarAppURL() { + openURL(url) + } + }) { + eventRow(event) } - }) { - eventRow(event) + .id(event.id) + .padding(.leading, -5) + .buttonStyle(PlainButtonStyle()) + .listRowSeparator(.automatic) + .listRowSeparatorTint(.gray.opacity(0.2)) + .listRowBackground(Color.clear) } - .id(event.id) - .padding(.leading, -5) - .buttonStyle(PlainButtonStyle()) - .listRowSeparator(.automatic) - .listRowSeparatorTint(.gray.opacity(0.2)) - .listRowBackground(Color.clear) } + .listStyle(.plain) + .scrollIndicators(.never) + .scrollContentBackground(.hidden) + .background(Color.clear) + + LinearGradient(colors: [Color.black.opacity(0.65), .clear], startPoint: .top, endPoint: .bottom) + .frame(height: 16) + .allowsHitTesting(false) + .alignmentGuide(.top) { d in d[.top] } + .frame(maxHeight: .infinity, alignment: .top) + + LinearGradient(colors: [.clear, Color.black.opacity(0.65)], startPoint: .top, endPoint: .bottom) + .frame(height: 16) + .allowsHitTesting(false) + .alignmentGuide(.bottom) { d in d[.bottom] } + .frame(maxHeight: .infinity, alignment: .bottom) } - .listStyle(.plain) - .scrollIndicators(.never) - .scrollContentBackground(.hidden) - .background(Color.clear) .onAppear { scrollToRelevantEvent(proxy: proxy) } diff --git a/DynamicIsland/components/Notch/NotchTimerView.swift b/DynamicIsland/components/Notch/NotchTimerView.swift index 92f006e..e3a2d9c 100644 --- a/DynamicIsland/components/Notch/NotchTimerView.swift +++ b/DynamicIsland/components/Notch/NotchTimerView.swift @@ -99,20 +99,34 @@ struct NotchTimerView: View { } else { let computedHeight = CGFloat(timerPresets.count) * 60 + 4 let listHeight = min(max(0, maxTabContentHeight - 16), computedHeight) - List { - ForEach(timerPresets) { preset in - TimerPresetCard(preset: preset, isActive: timerManager.activePresetId == preset.id) { - timerManager.startTimer(duration: preset.duration, name: preset.name, preset: preset) - coordinator.currentView = .timer + ZStack { + List { + ForEach(timerPresets) { preset in + TimerPresetCard(preset: preset, isActive: timerManager.activePresetId == preset.id) { + timerManager.startTimer(duration: preset.duration, name: preset.name, preset: preset) + coordinator.currentView = .timer + } + .listRowInsets(EdgeInsets(top: 2, leading: 0, bottom: 2, trailing: 0)) + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) } - .listRowInsets(EdgeInsets(top: 2, leading: 0, bottom: 2, trailing: 0)) - .listRowBackground(Color.clear) - .listRowSeparator(.hidden) } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .scrollIndicators(.never) + + LinearGradient(colors: [Color.black.opacity(0.65), .clear], startPoint: .top, endPoint: .bottom) + .frame(height: 16) + .allowsHitTesting(false) + .alignmentGuide(.top) { d in d[.top] } + .frame(maxHeight: .infinity, alignment: .top) + + LinearGradient(colors: [.clear, Color.black.opacity(0.65)], startPoint: .top, endPoint: .bottom) + .frame(height: 16) + .allowsHitTesting(false) + .alignmentGuide(.bottom) { d in d[.bottom] } + .frame(maxHeight: .infinity, alignment: .bottom) } - .listStyle(.plain) - .scrollContentBackground(.hidden) - .scrollIndicators(.never) .frame(height: listHeight) } } From 5a503b82386f114438736efd5e9dceb2c5cc0661 Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Thu, 11 Dec 2025 01:16:35 +0530 Subject: [PATCH 2/2] BugFix: Timer UI --- .../LockScreen/LockScreenTimerWidget.swift | 43 +++++++++++++++---- .../components/Notch/NotchTimerView.swift | 4 +- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/DynamicIsland/components/LockScreen/LockScreenTimerWidget.swift b/DynamicIsland/components/LockScreen/LockScreenTimerWidget.swift index ab2145d..9e7594d 100644 --- a/DynamicIsland/components/LockScreen/LockScreenTimerWidget.swift +++ b/DynamicIsland/components/LockScreen/LockScreenTimerWidget.swift @@ -20,12 +20,35 @@ struct LockScreenTimerWidget: View { } } - private var countdownFont: Font { displayFont(size: 56) } - private func displayFont(size: CGFloat) -> Font { .custom("SF Pro Display", size: size) } + private var hasHoursComponent: Bool { + abs(timerManager.remainingTime) >= 3600 + } + + private var hasDoubleDigitHours: Bool { + abs(timerManager.remainingTime) >= 36_000 // 10 hours or more + } + + private var titleFrameWidth: CGFloat { + if hasDoubleDigitHours { return 78 } + if hasHoursComponent { return 90 } + return 130 + } + + private var countdownFrameWidth: CGFloat { + if hasDoubleDigitHours { return 248 } + if hasHoursComponent { return 235 } + return 205 + } + + private var countdownFont: Font { + let baseSize: CGFloat = hasDoubleDigitHours ? 52 : 56 + return displayFont(size: baseSize) + } + private var timerLabel: String { timerManager.timerName.isEmpty ? "Timer" : timerManager.timerName } @@ -104,7 +127,7 @@ struct LockScreenTimerWidget: View { var body: some View { VStack(spacing: 16) { - HStack(alignment: .center, spacing: 18) { + HStack(alignment: .center, spacing: 8) { controlButtons titleSection @@ -113,7 +136,7 @@ struct LockScreenTimerWidget: View { countdownSection } } - .padding(.horizontal, 24) + .padding(.horizontal, 12) .padding(.vertical, 18) .frame(width: Self.preferredSize.width, height: Self.preferredSize.height) .background(widgetBackground) @@ -161,10 +184,13 @@ struct LockScreenTimerWidget: View { .foregroundStyle(timerManager.isOvertime ? Color.red : accentColor) .contentTransition(.numericText()) .animation(.smooth(duration: 0.25), value: timerManager.remainingTime) + .lineLimit(1) + .minimumScaleFactor(0.68) .frame(maxWidth: .infinity, alignment: .trailing) } - .frame(width: 220, alignment: .center) + .frame(width: countdownFrameWidth, alignment: .center) .padding(.trailing, 2) + .layoutPriority(2) } private var titleSection: some View { @@ -174,12 +200,13 @@ struct LockScreenTimerWidget: View { font: displayFont(size: 18), nsFont: .title3, textColor: accentColor, - minDuration: 0.18, - frameWidth: Self.preferredSize.width * 0.35 + minDuration: 0.16, + frameWidth: titleFrameWidth ) - .frame(maxWidth: .infinity) + .frame(maxWidth: titleFrameWidth) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) + .layoutPriority(0) } private var accentRibbon: some View { diff --git a/DynamicIsland/components/Notch/NotchTimerView.swift b/DynamicIsland/components/Notch/NotchTimerView.swift index e3a2d9c..f010c17 100644 --- a/DynamicIsland/components/Notch/NotchTimerView.swift +++ b/DynamicIsland/components/Notch/NotchTimerView.swift @@ -215,6 +215,8 @@ struct NotchTimerView: View { .foregroundStyle(timerManager.isOvertime ? Color.red : .white) .contentTransition(.numericText()) .animation(.smooth(duration: 0.25), value: timerManager.remainingTime) + .lineLimit(1) + .minimumScaleFactor(0.8) if timerManager.isOvertime { Text("Overtime") @@ -222,7 +224,7 @@ struct NotchTimerView: View { .foregroundStyle(.red) } } - .frame(width: 150, alignment: .trailing) + .frame(width: 190, alignment: .trailing) } @ViewBuilder