141 lines
6.3 KiB
Swift
141 lines
6.3 KiB
Swift
//
|
|
// 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)
|
|
}
|