diff --git a/TheNoiseClock/Core/Constants/AppConstants.swift b/TheNoiseClock/Core/Constants/AppConstants.swift index 0a770ff..4986f41 100644 --- a/TheNoiseClock/Core/Constants/AppConstants.swift +++ b/TheNoiseClock/Core/Constants/AppConstants.swift @@ -53,8 +53,9 @@ enum AppConstants { static let digitScale = 1.0 static let clockOpacity = 0.5 static let overlayOpacity = 0.5 - static let maxFontSize = 220.0 - static let safeInset = 16.0 // Reasonable safe inset + static let minFontSize = 20.0 + static let maxFontSize = 500.0 + static let safeInset = 4.0 // Reasonable safe inset } // MARK: - System Sounds diff --git a/TheNoiseClock/Core/Utilities/FontUtils.swift b/TheNoiseClock/Core/Utilities/FontUtils.swift index dc5579a..df0f46e 100644 --- a/TheNoiseClock/Core/Utilities/FontUtils.swift +++ b/TheNoiseClock/Core/Utilities/FontUtils.swift @@ -217,28 +217,7 @@ enum FontUtils { return .custom(family, size: size) .weight(fontWeight) } - - /// Calculate the maximum width for any two-digit combination - /// - Parameters: - /// - font: The font to measure with - /// - fontSize: The font size - /// - Returns: Maximum width for any two-digit combination - static func maxTwoDigitWidth(font: UIFont, fontSize: CGFloat) -> CGFloat { - let digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] - var maxWidth: CGFloat = 0 - // Test all possible two-digit combinations to find the widest - for firstDigit in digits { - for secondDigit in digits { - let combination = firstDigit + secondDigit - let width = measureTextSize(text: combination, font: font).width - maxWidth = max(maxWidth, width) - } - } - - return maxWidth - } - /// Get dot size multiplier to match visual weight of font /// - Parameter weight: Font weight name /// - Returns: Multiplier for dot size (0.3 to 1.0) - much smaller for lighter weights @@ -338,19 +317,11 @@ enum FontUtils { return (text as NSString).size(withAttributes: attributes) } - /// Get the maximum character width for digits to ensure consistent spacing - /// - Parameter font: The font to measure with - /// - Returns: The width of the widest digit character - static func maxDigitWidth(font: UIFont) -> CGFloat { - let digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] - var maxWidth: CGFloat = 0 - - for digit in digits { - let size = measureTextSize(text: digit, font: font) - maxWidth = max(maxWidth, size.width) - } - - return maxWidth + // Calculate height of text for the given font - this ensures consistent height + static func calculateMaxTextSize(font: UIFont) -> CGSize { + let attributes = [NSAttributedString.Key.font: font] + let size = ("8" as NSString).size(withAttributes: attributes) + return size } /// Calculate fixed-width layout size for time display to prevent jumping @@ -366,8 +337,9 @@ enum FontUtils { showAmPm: Bool, isPortrait: Bool ) -> (width: CGFloat, height: CGFloat) { - let digitWidth = maxDigitWidth(font: font) - let digitHeight = measureTextSize(text: "8", font: font).height // Use 8 as reference height + let digitSize = calculateMaxTextSize(font: font) + let digitWidth = digitSize.width + let digitHeight = digitSize.height // Calculate separator sizes let dotDiameter = font.pointSize * 0.20 @@ -429,12 +401,11 @@ enum FontUtils { isPortrait: Bool, showSeconds: Bool = false ) -> CGFloat { - // Use reasonable safe areas + // Calculate optimal size with reasonable space usage let safeInset = AppConstants.Defaults.safeInset let availableWidth = max(1, containerWidth - safeInset * 2) let availableHeight = max(1, containerHeight - safeInset * 2) - // Calculate optimal size with reasonable space usage let optimalSize: CGFloat if isPortrait { // In portrait, use most of the available height @@ -451,8 +422,7 @@ enum FontUtils { } // Apply reasonable bounds - let minSize: CGFloat = 20 - let maxSize: CGFloat = AppConstants.Defaults.maxFontSize - return max(minSize, min(optimalSize, maxSize)) + let minSize: CGFloat = AppConstants.Defaults.minFontSize + return max(minSize, optimalSize) } } diff --git a/TheNoiseClock/Views/Clock/Components/DigitView.swift b/TheNoiseClock/Views/Clock/Components/DigitView.swift index d3d94af..2666ab2 100644 --- a/TheNoiseClock/Views/Clock/Components/DigitView.swift +++ b/TheNoiseClock/Views/Clock/Components/DigitView.swift @@ -29,7 +29,7 @@ struct DigitView: View { .position(x: geometry.size.width / 2, y: geometry.size.height / 2) } } - .frame(width: digitWidth, height: digitHeight) + .frame(maxWidth: .infinity, maxHeight: .infinity) } // MARK: - Computed Properties diff --git a/TheNoiseClock/Views/Clock/Components/TimeDisplayView.swift b/TheNoiseClock/Views/Clock/Components/TimeDisplayView.swift index 88a61bf..5c8f613 100644 --- a/TheNoiseClock/Views/Clock/Components/TimeDisplayView.swift +++ b/TheNoiseClock/Views/Clock/Components/TimeDisplayView.swift @@ -56,76 +56,40 @@ struct TimeDisplayView: View { // MARK: - Body var body: some View { GeometryReader { proxy in - let size = proxy.size - let portrait = size.height >= size.width - - // Get safe area information for font sizing to avoid Dynamic Island overlap - let safeAreaInsets = proxy.safeAreaInsets - let safeWidth = size.width - safeAreaInsets.leading - safeAreaInsets.trailing // availableWidth - let safeHeight = size.height - safeAreaInsets.top - safeAreaInsets.bottom // availableHeight - let fullScreenSize = CGSize(width: safeWidth, height: safeHeight) + let containerSize = proxy.size + let portrait = containerSize.height >= containerSize.width - // Use optimal font sizing that maximizes space usage with full screen + // Use optimal font sizing based on actual container size let baseFontSize = stretched ? FontUtils.maximumStretchedFontSize( - containerWidth: fullScreenSize.width, - containerHeight: fullScreenSize.height, + containerWidth: containerSize.width, + containerHeight: containerSize.height, isPortrait: portrait, showSeconds: showSeconds ) : FontUtils.optimalFontSize( - containerWidth: fullScreenSize.width, - containerHeight: fullScreenSize.height, + containerWidth: containerSize.width, + containerHeight: containerSize.height, isPortrait: portrait, showSeconds: showSeconds ) + // DEBUG: Print font size and container size + let _ = print("DEBUG: Container: \(containerSize), Font: \(baseFontSize), Stretched: \(stretched)") + // Time components let hour = use24Hour ? Self.hour24DF.string(from: date) : Self.hour12DF.string(from: date) let minute = Self.minuteDF.string(from: date) let secondsText = Self.secondDF.string(from: date) - // Calculate sizes using fixed-width approach to prevent jumping - let digitUIFont = FontUtils.customUIFont( - size: baseFontSize, - family: fontFamily, - weight: fontWeight, - design: fontDesign - ) - - // Calculate consistent sizes for layout - let textHeight = measureText("8", font: digitUIFont).height // Use 8 as reference height // Separators - reasonable spacing with extra padding in landscape let dotDiameter = baseFontSize * 0.20 let hSpacing = portrait ? baseFontSize * 0.18 : baseFontSize * 0.25 // More spacing in landscape let vSpacing = portrait ? baseFontSize * 0.22 : baseFontSize * 0.30 // More spacing in landscape - let horizontalSepSize = CGSize(width: dotDiameter * 2 + hSpacing, height: dotDiameter) - let verticalSepSize = CGSize(width: dotDiameter, height: dotDiameter * 2 + vSpacing) - // Calculate layout - simplified without AM/PM - let (totalWidth, totalHeight) = calculateLayoutSize( - portrait: portrait, - horizontalSepSize: horizontalSepSize, - verticalSepSize: verticalSepSize, - showSeconds: showSeconds - ) - - // Calculate scale with maximum space utilization using full screen - let safeInset = AppConstants.Defaults.safeInset - 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) - let heightScale = availableH / max(totalHeight, 1) - - // For stretched mode, use reasonable scaling - let effectiveScale = stretched ? - max(0.1, min(min(widthScale, heightScale), 1.5)) : // Use min scale and cap at 1.5x to prevent overflow - max(0.1, max(0.1, min(widthScale, heightScale)) * CGFloat(max(0.1, min(manualScale, 1.0)))) - - let finalScale = effectiveScale + // Simple scaling - let the content size naturally and apply manual scale + let finalScale = stretched ? 1.0 : CGFloat(max(0.1, min(manualScale, 1.0))) // Time display with consistent centering and stable layout Group { @@ -158,32 +122,12 @@ struct TimeDisplayView: View { .minimumScaleFactor(0.1) .clipped() // Prevent overflow beyond bounds } - .border(.blue, width: 1) + .border(Color.blue, width: 1) .frame(maxWidth: .infinity, maxHeight: .infinity) .onOrientationChange() // Force updates on orientation changes } - // MARK: - Helper Methods - private func measureText(_ text: String, font: UIFont) -> CGSize { - return FontUtils.measureTextSize(text: text, font: font) - } - private func calculateLayoutSize( - portrait: Bool, - horizontalSepSize: CGSize, - verticalSepSize: CGSize, - showSeconds: Bool - ) -> (CGFloat, CGFloat) { - // Simplified layout calculation without AM/PM - // This is just a placeholder since we're using natural sizing now - if portrait { - let totalH = horizontalSepSize.height * (showSeconds ? 2 : 1) - return (0, totalH) // Width will be determined by content - } else { - let totalW = verticalSepSize.width * (showSeconds ? 2 : 1) - return (totalW, 0) // Height will be determined by content - } - } } diff --git a/TheNoiseClock/Views/Clock/Components/TimeSegment.swift b/TheNoiseClock/Views/Clock/Components/TimeSegment.swift index fd47743..5b130f0 100644 --- a/TheNoiseClock/Views/Clock/Components/TimeSegment.swift +++ b/TheNoiseClock/Views/Clock/Components/TimeSegment.swift @@ -33,8 +33,10 @@ struct TimeSegment: View { digitWidth: singleDigitWidth, digitHeight: singleDigitHeight ) + .border(Color.yellow, width: 1) } } + .frame(maxWidth: .infinity, maxHeight: .infinity) } // MARK: - Computed Properties diff --git a/TheNoiseClock/Views/Clock/Components/TopOverlayView.swift b/TheNoiseClock/Views/Clock/Components/TopOverlayView.swift index a9a8634..fad59a1 100644 --- a/TheNoiseClock/Views/Clock/Components/TopOverlayView.swift +++ b/TheNoiseClock/Views/Clock/Components/TopOverlayView.swift @@ -39,7 +39,6 @@ struct TopOverlayView: View { } .padding(.horizontal, UIConstants.Spacing.medium) .padding(.vertical, UIConstants.Spacing.small) - .cardStyle() .transition(.opacity) .onAppear { batteryService.startMonitoring()