From 2d4d76cc8df7bce61d954b10827877ac7a7c6065 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 8 Sep 2025 11:21:34 -0500 Subject: [PATCH] Signed-off-by: Matt Bruce --- TheNoiseClock/Core/Utilities/FontUtils.swift | 28 +++++++++ TheNoiseClock/Views/Clock/ClockView.swift | 4 +- .../Clock/Components/TimeDisplayView.swift | 57 ++++++++++--------- 3 files changed, 60 insertions(+), 29 deletions(-) diff --git a/TheNoiseClock/Core/Utilities/FontUtils.swift b/TheNoiseClock/Core/Utilities/FontUtils.swift index dc89521..a46a7fe 100644 --- a/TheNoiseClock/Core/Utilities/FontUtils.swift +++ b/TheNoiseClock/Core/Utilities/FontUtils.swift @@ -207,6 +207,34 @@ enum FontUtils { ) } + /// Get weight multiplier for visual consistency with font weight + /// - Parameter weight: Font weight name + /// - Returns: Multiplier for dot size (0.7 to 1.3) + static func weightMultiplier(for weight: String) -> CGFloat { + switch weight { + case "Ultra Light": + return 0.7 + case "Thin": + return 0.75 + case "Light": + return 0.8 + case "Regular": + return 0.85 + case "Medium": + return 0.9 + case "Semibold": + return 1.0 + case "Bold": + return 1.1 + case "Heavy": + return 1.2 + case "Black": + return 1.3 + default: + return 1.0 + } + } + /// Create a UIFont with specified parameters for measurements /// - Parameters: /// - size: Font size diff --git a/TheNoiseClock/Views/Clock/ClockView.swift b/TheNoiseClock/Views/Clock/ClockView.swift index ac6caf5..ac2f6d1 100644 --- a/TheNoiseClock/Views/Clock/ClockView.swift +++ b/TheNoiseClock/Views/Clock/ClockView.swift @@ -22,7 +22,7 @@ struct ClockView: View { GeometryReader { geometry in ZStack { - // Time display - fills available space within safe areas + // Time display - fills entire available space TimeDisplayView( date: viewModel.currentTime, use24Hour: viewModel.style.use24Hour, @@ -37,7 +37,6 @@ struct ClockView: View { fontWeight: viewModel.style.fontWeight, fontDesign: viewModel.style.fontDesign ) - .frame(width: geometry.size.width, height: geometry.size.height) .transition(.opacity) // Top overlay - positioned at top with safe area consideration @@ -60,6 +59,7 @@ struct ClockView: View { .animation(UIConstants.AnimationCurves.smooth, value: viewModel.isDisplayMode) } } + .ignoresSafeArea(.all, edges: viewModel.isDisplayMode ? .bottom : []) .navigationTitle(viewModel.isDisplayMode ? "" : "Clock") .toolbar { if !viewModel.isDisplayMode { diff --git a/TheNoiseClock/Views/Clock/Components/TimeDisplayView.swift b/TheNoiseClock/Views/Clock/Components/TimeDisplayView.swift index 6870d3d..f713d5c 100644 --- a/TheNoiseClock/Views/Clock/Components/TimeDisplayView.swift +++ b/TheNoiseClock/Views/Clock/Components/TimeDisplayView.swift @@ -66,29 +66,26 @@ struct TimeDisplayView: View { let size = proxy.size let portrait = size.height >= size.width - // Get safe area information to avoid Dynamic Island overlap - let safeAreaInsets = proxy.safeAreaInsets - let availableWidth = size.width - safeAreaInsets.leading - safeAreaInsets.trailing - let availableHeight = size.height - safeAreaInsets.top - safeAreaInsets.bottom - - // Use available space within safe areas for font sizing calculations - let availableSize = CGSize(width: availableWidth, height: availableHeight) - - // Use full GeometryReader size for frame (to fill entire space) + // Use the full GeometryReader size for maximum space usage let fullScreenSize = size - // Use optimal font sizing that maximizes space usage within safe areas + // Get safe area information for font sizing to avoid Dynamic Island overlap + let safeAreaInsets = proxy.safeAreaInsets + let _ = size.width - safeAreaInsets.leading - safeAreaInsets.trailing // availableWidth + let _ = size.height - safeAreaInsets.top - safeAreaInsets.bottom // availableHeight + + // Use optimal font sizing that maximizes space usage with full screen let baseFontSize = stretched ? FontUtils.maximumStretchedFontSize( - containerWidth: availableSize.width, - containerHeight: availableSize.height, + containerWidth: fullScreenSize.width, + containerHeight: fullScreenSize.height, isPortrait: portrait, showSeconds: showSeconds, showAmPm: !use24Hour && showAmPmBadge ) : FontUtils.optimalFontSize( - containerWidth: availableSize.width, - containerHeight: availableSize.height, + containerWidth: fullScreenSize.width, + containerHeight: fullScreenSize.height, isPortrait: portrait, showSeconds: showSeconds, showAmPm: !use24Hour && showAmPmBadge @@ -146,10 +143,10 @@ struct TimeDisplayView: View { showAMPM: showAMPM ) - // Calculate scale with maximum space utilization using available space + // Calculate scale with maximum space utilization using full screen let safeInset = AppConstants.Defaults.safeInset - let availableW = max(1, availableSize.width - safeInset * 2) - let availableH = max(1, availableSize.height - safeInset * 2) + let availableW = max(1, fullScreenSize.width - safeInset * 2) + let availableH = max(1, fullScreenSize.height - safeInset * 2) // Calculate scaling factors let widthScale = availableW / max(totalWidth, 1) @@ -170,11 +167,11 @@ struct TimeDisplayView: View { if showAMPM { TimeSegment(text: ampmText, fontSize: ampmFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign) } else { - HorizontalColon(dotDiameter: dotDiameter, spacing: hSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity) + HorizontalColon(dotDiameter: dotDiameter, spacing: hSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight) } TimeSegment(text: minute, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign) if showSeconds { - HorizontalColon(dotDiameter: dotDiameter, spacing: hSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity) + HorizontalColon(dotDiameter: dotDiameter, spacing: hSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight) TimeSegment(text: secondsText, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign) } else { // Invisible placeholder to maintain consistent spacing @@ -188,11 +185,11 @@ struct TimeDisplayView: View { if showAMPM { TimeSegment(text: ampmText, fontSize: ampmFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign) } else { - VerticalColon(dotDiameter: dotDiameter, spacing: vSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity) + VerticalColon(dotDiameter: dotDiameter, spacing: vSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight) } TimeSegment(text: minute, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign) if showSeconds { - VerticalColon(dotDiameter: dotDiameter, spacing: vSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity) + VerticalColon(dotDiameter: dotDiameter, spacing: vSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight) TimeSegment(text: secondsText, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign) } else { // Invisible placeholder to maintain consistent spacing @@ -331,12 +328,13 @@ private struct HorizontalColon: View { let opacity: Double let digitColor: Color let glowIntensity: Double + let fontWeight: String var body: some View { let clamped = ColorUtils.clampOpacity(opacity) HStack(spacing: spacing) { - DotCircle(size: dotDiameter, opacity: clamped, digitColor: digitColor, glowIntensity: glowIntensity) - DotCircle(size: dotDiameter, opacity: clamped, digitColor: digitColor, glowIntensity: glowIntensity) + DotCircle(size: dotDiameter, opacity: clamped, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight) + DotCircle(size: dotDiameter, opacity: clamped, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight) } .fixedSize(horizontal: true, vertical: true) .accessibilityHidden(true) @@ -349,12 +347,13 @@ private struct VerticalColon: View { let opacity: Double let digitColor: Color let glowIntensity: Double + let fontWeight: String var body: some View { let clamped = ColorUtils.clampOpacity(opacity) VStack(spacing: spacing) { - DotCircle(size: dotDiameter, opacity: clamped, digitColor: digitColor, glowIntensity: glowIntensity) - DotCircle(size: dotDiameter, opacity: clamped, digitColor: digitColor, glowIntensity: glowIntensity) + DotCircle(size: dotDiameter, opacity: clamped, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight) + DotCircle(size: dotDiameter, opacity: clamped, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight) } .fixedSize(horizontal: true, vertical: true) .accessibilityHidden(true) @@ -366,17 +365,21 @@ private struct DotCircle: View { let opacity: Double let digitColor: Color let glowIntensity: Double + let fontWeight: String var body: some View { + let weightMultiplier = FontUtils.weightMultiplier(for: fontWeight) + let adjustedSize = size * weightMultiplier + ZStack { Circle() .fill(digitColor) - .frame(width: size, height: size) + .frame(width: adjustedSize, height: adjustedSize) .blur(radius: ColorUtils.glowRadius(intensity: glowIntensity)) .opacity(ColorUtils.glowOpacity(intensity: glowIntensity) * opacity) Circle() .fill(digitColor) - .frame(width: size, height: size) + .frame(width: adjustedSize, height: adjustedSize) .opacity(opacity) } }