// // TimeDisplayView.swift // TheNoiseClock // // Created by Matt Bruce on 9/7/25. // import SwiftUI /// Component for displaying segmented time with customizable formatting struct TimeDisplayView: View { // MARK: - Properties let date: Date let use24Hour: Bool let showSeconds: Bool let digitColor: Color let glowIntensity: Double let manualScale: Double let stretched: Bool let clockOpacity: Double let fontFamily: FontFamily let fontWeight: Font.Weight let fontDesign: Font.Design let forceHorizontalMode: Bool @State var fontSize: CGFloat = 1000 // MARK: - Formatters private static let hour24DF: DateFormatter = { let df = DateFormatter() df.locale = Locale(identifier: "en_US_POSIX") df.dateFormat = "HH" return df }() private static let hour12DF: DateFormatter = { let df = DateFormatter() df.locale = Locale(identifier: "en_US_POSIX") df.dateFormat = "h" return df }() private static let minuteDF: DateFormatter = { let df = DateFormatter() df.locale = Locale(identifier: "en_US_POSIX") df.dateFormat = "mm" return df }() private static let secondDF: DateFormatter = { let df = DateFormatter() df.locale = Locale(identifier: "en_US_POSIX") df.dateFormat = "ss" return df }() // MARK: - Body var body: some View { GeometryReader { proxy in let containerSize = proxy.size let portraitMode = containerSize.height >= containerSize.width let portrait = !forceHorizontalMode && containerSize.height >= containerSize.width // 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) // Separators - reasonable spacing with extra padding in landscape let dotDiameter = fontSize * 0.75 let dotSpacing = portrait ? fontSize * 0.18 : fontSize * 0.25 // More spacing in landscape // 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 { if portrait { VStack(alignment: .center) { TimeSegment(text: hour, fontSize: $fontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign) ColonView(dotDiameter: dotDiameter, spacing: dotSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight, isHorizontal: true) TimeSegment(text: minute, fontSize: $fontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign) if showSeconds { ColonView(dotDiameter: dotDiameter, spacing: dotSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight, isHorizontal: true) TimeSegment(text: secondsText, fontSize: $fontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign) } } } else { HStack(alignment: .center) { TimeSegment(text: hour, fontSize: $fontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign) ColonView(dotDiameter: dotDiameter, spacing: dotSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight, isHorizontal: false) TimeSegment(text: minute, fontSize: $fontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign) if showSeconds { ColonView(dotDiameter: dotDiameter, spacing: dotSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight, isHorizontal: false) TimeSegment(text: secondsText, fontSize: $fontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign) } } .frame(maxWidth: .infinity) } } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) .offset(y: portraitMode && forceHorizontalMode ? -containerSize.height * 0.10 : 0) // Push up in horizontal mode .scaleEffect(finalScale, anchor: .center) .animation(UIConstants.AnimationCurves.smooth, value: finalScale) .animation(UIConstants.AnimationCurves.smooth, value: showSeconds) // Smooth animation for seconds toggle .minimumScaleFactor(0.1) .clipped() // Prevent overflow beyond bounds } //.border(.yellow, width: 1) .frame(maxWidth: .infinity, maxHeight: .infinity) .onOrientationChange() // Force updates on orientation changes } } // MARK: - Preview #Preview { let style = ClockStyle() style.fontFamily = .verdana style.fontWeight = .medium return TimeDisplayView( date: Date(), use24Hour: style.use24Hour, showSeconds: style.showSeconds, digitColor: style.digitColor, glowIntensity: style.glowIntensity, manualScale: style.digitScale, stretched: style.stretched, clockOpacity: style.clockOpacity, fontFamily: style.fontFamily, fontWeight: style.fontWeight, fontDesign: style.fontDesign, forceHorizontalMode: style.forceHorizontalMode ) .background(Color.black) }