Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
2fafae909d
commit
c8fa702382
@ -121,6 +121,147 @@ enum FontUtils {
|
||||
return baseFontSize * 0.20
|
||||
}
|
||||
|
||||
// MARK: - Font Customization
|
||||
|
||||
/// Convert font family string to Font
|
||||
/// - Parameter family: Font family name
|
||||
/// - Returns: SwiftUI Font
|
||||
static func fontFamily(_ family: String) -> Font {
|
||||
switch family {
|
||||
case "San Francisco":
|
||||
return .system(.body, design: .default)
|
||||
case "System":
|
||||
return .system(.body, design: .default)
|
||||
case "Monaco":
|
||||
return .system(.body, design: .monospaced)
|
||||
case "Courier":
|
||||
return .system(.body, design: .monospaced)
|
||||
default:
|
||||
return .system(.body, design: .default)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert font weight string to Font.Weight
|
||||
/// - Parameter weight: Font weight name
|
||||
/// - Returns: Font.Weight
|
||||
static func fontWeight(_ weight: String) -> Font.Weight {
|
||||
switch weight {
|
||||
case "Ultra Light":
|
||||
return .ultraLight
|
||||
case "Thin":
|
||||
return .thin
|
||||
case "Light":
|
||||
return .light
|
||||
case "Regular":
|
||||
return .regular
|
||||
case "Medium":
|
||||
return .medium
|
||||
case "Semibold":
|
||||
return .semibold
|
||||
case "Bold":
|
||||
return .bold
|
||||
case "Heavy":
|
||||
return .heavy
|
||||
case "Black":
|
||||
return .black
|
||||
default:
|
||||
return .bold
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert font design string to Font.Design
|
||||
/// - Parameter design: Font design name
|
||||
/// - Returns: Font.Design
|
||||
static func fontDesign(_ design: String) -> Font.Design {
|
||||
switch design {
|
||||
case "Default":
|
||||
return .default
|
||||
case "Serif":
|
||||
return .serif
|
||||
case "Rounded":
|
||||
return .rounded
|
||||
case "Monospaced":
|
||||
return .monospaced
|
||||
default:
|
||||
return .rounded
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a custom font with specified parameters
|
||||
/// - Parameters:
|
||||
/// - size: Font size
|
||||
/// - family: Font family name
|
||||
/// - weight: Font weight name
|
||||
/// - design: Font design name
|
||||
/// - Returns: SwiftUI Font
|
||||
static func customFont(
|
||||
size: CGFloat,
|
||||
family: String,
|
||||
weight: String,
|
||||
design: String
|
||||
) -> Font {
|
||||
return .system(
|
||||
size: size,
|
||||
weight: fontWeight(weight),
|
||||
design: fontDesign(design)
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a UIFont with specified parameters for measurements
|
||||
/// - Parameters:
|
||||
/// - size: Font size
|
||||
/// - family: Font family name
|
||||
/// - weight: Font weight name
|
||||
/// - design: Font design name
|
||||
/// - Returns: UIFont
|
||||
static func customUIFont(
|
||||
size: CGFloat,
|
||||
family: String,
|
||||
weight: String,
|
||||
design: String
|
||||
) -> UIFont {
|
||||
let uiWeight: UIFont.Weight
|
||||
switch weight {
|
||||
case "Ultra Light":
|
||||
uiWeight = .ultraLight
|
||||
case "Thin":
|
||||
uiWeight = .thin
|
||||
case "Light":
|
||||
uiWeight = .light
|
||||
case "Regular":
|
||||
uiWeight = .regular
|
||||
case "Medium":
|
||||
uiWeight = .medium
|
||||
case "Semibold":
|
||||
uiWeight = .semibold
|
||||
case "Bold":
|
||||
uiWeight = .bold
|
||||
case "Heavy":
|
||||
uiWeight = .heavy
|
||||
case "Black":
|
||||
uiWeight = .black
|
||||
default:
|
||||
uiWeight = .bold
|
||||
}
|
||||
|
||||
let uiDesign: UIFontDescriptor.SystemDesign
|
||||
switch design {
|
||||
case "Serif":
|
||||
uiDesign = .serif
|
||||
case "Rounded":
|
||||
uiDesign = .rounded
|
||||
case "Monospaced":
|
||||
uiDesign = .monospaced
|
||||
default:
|
||||
uiDesign = .default
|
||||
}
|
||||
|
||||
let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body)
|
||||
.withDesign(uiDesign) ?? UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body)
|
||||
|
||||
return UIFont(descriptor: descriptor.addingAttributes([.traits: [UIFontDescriptor.TraitKey.weight: uiWeight]]), size: size)
|
||||
}
|
||||
|
||||
/// Measure text size with given font
|
||||
/// - Parameters:
|
||||
/// - text: Text to measure
|
||||
|
||||
@ -25,6 +25,11 @@ class ClockStyle: Codable, Equatable {
|
||||
var stretched: Bool = true
|
||||
var backgroundHex: String = AppConstants.Defaults.backgroundColorHex
|
||||
|
||||
// MARK: - Font Settings
|
||||
var fontFamily: String = "System" // System, San Francisco, etc.
|
||||
var fontWeight: String = "Bold" // Ultra Light, Thin, Light, Regular, Medium, Semibold, Bold, Heavy, Black
|
||||
var fontDesign: String = "Rounded" // Default, Serif, Rounded, Monospaced
|
||||
|
||||
// MARK: - Overlay Settings
|
||||
var showBattery: Bool = true
|
||||
var showDate: Bool = true
|
||||
@ -46,6 +51,9 @@ class ClockStyle: Codable, Equatable {
|
||||
case digitScale
|
||||
case stretched
|
||||
case backgroundHex
|
||||
case fontFamily
|
||||
case fontWeight
|
||||
case fontDesign
|
||||
case showBattery
|
||||
case showDate
|
||||
case clockOpacity
|
||||
@ -70,6 +78,9 @@ class ClockStyle: Codable, Equatable {
|
||||
self.digitScale = try container.decodeIfPresent(Double.self, forKey: .digitScale) ?? self.digitScale
|
||||
self.stretched = try container.decodeIfPresent(Bool.self, forKey: .stretched) ?? self.stretched
|
||||
self.backgroundHex = try container.decodeIfPresent(String.self, forKey: .backgroundHex) ?? self.backgroundHex
|
||||
self.fontFamily = try container.decodeIfPresent(String.self, forKey: .fontFamily) ?? self.fontFamily
|
||||
self.fontWeight = try container.decodeIfPresent(String.self, forKey: .fontWeight) ?? self.fontWeight
|
||||
self.fontDesign = try container.decodeIfPresent(String.self, forKey: .fontDesign) ?? self.fontDesign
|
||||
self.showBattery = try container.decodeIfPresent(Bool.self, forKey: .showBattery) ?? self.showBattery
|
||||
self.showDate = try container.decodeIfPresent(Bool.self, forKey: .showDate) ?? self.showDate
|
||||
self.clockOpacity = try container.decodeIfPresent(Double.self, forKey: .clockOpacity) ?? self.clockOpacity
|
||||
@ -89,6 +100,9 @@ class ClockStyle: Codable, Equatable {
|
||||
try container.encode(digitScale, forKey: .digitScale)
|
||||
try container.encode(stretched, forKey: .stretched)
|
||||
try container.encode(backgroundHex, forKey: .backgroundHex)
|
||||
try container.encode(fontFamily, forKey: .fontFamily)
|
||||
try container.encode(fontWeight, forKey: .fontWeight)
|
||||
try container.encode(fontDesign, forKey: .fontDesign)
|
||||
try container.encode(showBattery, forKey: .showBattery)
|
||||
try container.encode(showDate, forKey: .showDate)
|
||||
try container.encode(clockOpacity, forKey: .clockOpacity)
|
||||
@ -131,6 +145,9 @@ class ClockStyle: Codable, Equatable {
|
||||
lhs.digitScale == rhs.digitScale &&
|
||||
lhs.stretched == rhs.stretched &&
|
||||
lhs.backgroundHex == rhs.backgroundHex &&
|
||||
lhs.fontFamily == rhs.fontFamily &&
|
||||
lhs.fontWeight == rhs.fontWeight &&
|
||||
lhs.fontDesign == rhs.fontDesign &&
|
||||
lhs.showBattery == rhs.showBattery &&
|
||||
lhs.showDate == rhs.showDate &&
|
||||
lhs.clockOpacity == rhs.clockOpacity &&
|
||||
|
||||
@ -28,6 +28,7 @@ struct ClockSettingsView: View {
|
||||
NavigationView {
|
||||
Form {
|
||||
TimeFormatSection(style: $style)
|
||||
FontSection(style: $style)
|
||||
AppearanceSection(
|
||||
style: $style,
|
||||
digitColor: $digitColor,
|
||||
@ -56,6 +57,57 @@ struct ClockSettingsView: View {
|
||||
}
|
||||
|
||||
// MARK: - Supporting Views
|
||||
private struct FontSection: View {
|
||||
@Binding var style: ClockStyle
|
||||
|
||||
private let fontFamilies = ["System", "San Francisco", "Monaco", "Courier"]
|
||||
private let fontWeights = ["Ultra Light", "Thin", "Light", "Regular", "Medium", "Semibold", "Bold", "Heavy", "Black"]
|
||||
private let fontDesigns = ["Default", "Serif", "Rounded", "Monospaced"]
|
||||
|
||||
var body: some View {
|
||||
Section(header: Text("Font")) {
|
||||
// Font Family
|
||||
Picker("Family", selection: $style.fontFamily) {
|
||||
ForEach(fontFamilies, id: \.self) { family in
|
||||
Text(family).tag(family)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.menu)
|
||||
|
||||
// Font Weight
|
||||
Picker("Weight", selection: $style.fontWeight) {
|
||||
ForEach(fontWeights, id: \.self) { weight in
|
||||
Text(weight).tag(weight)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.menu)
|
||||
|
||||
// Font Design
|
||||
Picker("Design", selection: $style.fontDesign) {
|
||||
ForEach(fontDesigns, id: \.self) { design in
|
||||
Text(design).tag(design)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.menu)
|
||||
|
||||
// Font Preview
|
||||
HStack {
|
||||
Text("Preview:")
|
||||
.foregroundColor(.secondary)
|
||||
Spacer()
|
||||
Text("12:34")
|
||||
.font(FontUtils.customFont(
|
||||
size: 24,
|
||||
family: style.fontFamily,
|
||||
weight: style.fontWeight,
|
||||
design: style.fontDesign
|
||||
))
|
||||
.foregroundColor(.primary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct TimeFormatSection: View {
|
||||
@Binding var style: ClockStyle
|
||||
|
||||
|
||||
@ -20,41 +20,45 @@ struct ClockView: View {
|
||||
viewModel.style.backgroundColor
|
||||
.ignoresSafeArea()
|
||||
|
||||
VStack(spacing: UIConstants.Spacing.medium) {
|
||||
// Top overlay
|
||||
if viewModel.style.showBattery || viewModel.style.showDate {
|
||||
TopOverlayView(
|
||||
showBattery: viewModel.style.showBattery,
|
||||
showDate: viewModel.style.showDate,
|
||||
color: viewModel.style.digitColor.opacity(UIConstants.Opacity.primary),
|
||||
opacity: viewModel.style.overlayOpacity
|
||||
GeometryReader { geometry in
|
||||
ZStack {
|
||||
// Time display - fills available space within safe areas
|
||||
TimeDisplayView(
|
||||
date: viewModel.currentTime,
|
||||
use24Hour: viewModel.style.use24Hour,
|
||||
showSeconds: viewModel.style.showSeconds,
|
||||
digitColor: viewModel.style.digitColor,
|
||||
glowIntensity: viewModel.style.glowIntensity,
|
||||
manualScale: viewModel.style.digitScale,
|
||||
stretched: viewModel.style.stretched,
|
||||
showAmPmBadge: viewModel.style.showAmPmBadge,
|
||||
clockOpacity: viewModel.style.clockOpacity,
|
||||
fontFamily: viewModel.style.fontFamily,
|
||||
fontWeight: viewModel.style.fontWeight,
|
||||
fontDesign: viewModel.style.fontDesign
|
||||
)
|
||||
.padding(.top, UIConstants.Spacing.small)
|
||||
.padding(.horizontal, UIConstants.Spacing.large)
|
||||
.frame(width: geometry.size.width, height: geometry.size.height)
|
||||
.transition(.opacity)
|
||||
|
||||
// Top overlay - positioned at top with safe area consideration
|
||||
VStack {
|
||||
if viewModel.style.showBattery || viewModel.style.showDate {
|
||||
TopOverlayView(
|
||||
showBattery: viewModel.style.showBattery,
|
||||
showDate: viewModel.style.showDate,
|
||||
color: viewModel.style.digitColor.opacity(UIConstants.Opacity.primary),
|
||||
opacity: viewModel.style.overlayOpacity
|
||||
)
|
||||
.padding(.top, UIConstants.Spacing.small)
|
||||
.padding(.horizontal, UIConstants.Spacing.large)
|
||||
.transition(.opacity)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
// Time display
|
||||
TimeDisplayView(
|
||||
date: viewModel.currentTime,
|
||||
use24Hour: viewModel.style.use24Hour,
|
||||
showSeconds: viewModel.style.showSeconds,
|
||||
digitColor: viewModel.style.digitColor,
|
||||
glowIntensity: viewModel.style.glowIntensity,
|
||||
manualScale: viewModel.style.digitScale,
|
||||
stretched: viewModel.style.stretched,
|
||||
showAmPmBadge: viewModel.style.showAmPmBadge,
|
||||
clockOpacity: viewModel.style.clockOpacity
|
||||
)
|
||||
.padding(.horizontal, UIConstants.Spacing.medium)
|
||||
.transition(.opacity)
|
||||
|
||||
Spacer()
|
||||
.scaleEffect(viewModel.isDisplayMode ? 1.0 : 0.995)
|
||||
.animation(UIConstants.AnimationCurves.smooth, value: viewModel.isDisplayMode)
|
||||
}
|
||||
.scaleEffect(viewModel.isDisplayMode ? 1.0 : 0.995)
|
||||
.animation(UIConstants.AnimationCurves.smooth, value: viewModel.isDisplayMode)
|
||||
}
|
||||
.navigationTitle(viewModel.isDisplayMode ? "" : "Clock")
|
||||
.toolbar {
|
||||
@ -72,6 +76,7 @@ struct ClockView: View {
|
||||
}
|
||||
.navigationBarBackButtonHidden(viewModel.isDisplayMode)
|
||||
.toolbar(viewModel.isDisplayMode ? .hidden : .automatic)
|
||||
.statusBarHidden(viewModel.isDisplayMode)
|
||||
.onAppear {
|
||||
setTabBarHidden(viewModel.isDisplayMode, animated: false)
|
||||
}
|
||||
@ -90,6 +95,7 @@ struct ClockView: View {
|
||||
.onEnded { _ in
|
||||
viewModel.toggleDisplayMode()
|
||||
setTabBarHidden(viewModel.isDisplayMode, animated: true)
|
||||
// Status bar hiding is handled by the .statusBarHidden modifier
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -20,6 +20,9 @@ struct TimeDisplayView: View {
|
||||
let stretched: Bool
|
||||
let showAmPmBadge: Bool
|
||||
let clockOpacity: Double
|
||||
let fontFamily: String
|
||||
let fontWeight: String
|
||||
let fontDesign: String
|
||||
|
||||
// MARK: - Formatters
|
||||
private static let hour24DF: DateFormatter = {
|
||||
@ -63,18 +66,29 @@ struct TimeDisplayView: View {
|
||||
let size = proxy.size
|
||||
let portrait = size.height >= size.width
|
||||
|
||||
// Use optimal font sizing that maximizes space usage
|
||||
// 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)
|
||||
let fullScreenSize = size
|
||||
|
||||
// Use optimal font sizing that maximizes space usage within safe areas
|
||||
let baseFontSize = stretched ?
|
||||
FontUtils.maximumStretchedFontSize(
|
||||
containerWidth: size.width,
|
||||
containerHeight: size.height,
|
||||
containerWidth: availableSize.width,
|
||||
containerHeight: availableSize.height,
|
||||
isPortrait: portrait,
|
||||
showSeconds: showSeconds,
|
||||
showAmPm: !use24Hour && showAmPmBadge
|
||||
) :
|
||||
FontUtils.optimalFontSize(
|
||||
containerWidth: size.width,
|
||||
containerHeight: size.height,
|
||||
containerWidth: availableSize.width,
|
||||
containerHeight: availableSize.height,
|
||||
isPortrait: portrait,
|
||||
showSeconds: showSeconds,
|
||||
showAmPm: !use24Hour && showAmPmBadge
|
||||
@ -89,8 +103,18 @@ struct TimeDisplayView: View {
|
||||
let showAMPM = !use24Hour && showAmPmBadge
|
||||
|
||||
// Calculate sizes using fixed-width approach to prevent jumping
|
||||
let digitUIFont = UIFont.systemFont(ofSize: baseFontSize, weight: .bold)
|
||||
let ampmUIFont = UIFont.systemFont(ofSize: ampmFontSize, weight: .bold)
|
||||
let digitUIFont = FontUtils.customUIFont(
|
||||
size: baseFontSize,
|
||||
family: fontFamily,
|
||||
weight: fontWeight,
|
||||
design: fontDesign
|
||||
)
|
||||
let ampmUIFont = FontUtils.customUIFont(
|
||||
size: ampmFontSize,
|
||||
family: fontFamily,
|
||||
weight: fontWeight,
|
||||
design: fontDesign
|
||||
)
|
||||
|
||||
// Use fixed-width calculations to prevent layout jumping
|
||||
let digitWidth = FontUtils.maxDigitWidth(font: digitUIFont)
|
||||
@ -122,10 +146,10 @@ struct TimeDisplayView: View {
|
||||
showAMPM: showAMPM
|
||||
)
|
||||
|
||||
// Calculate scale with maximum space utilization
|
||||
// Calculate scale with maximum space utilization using available space
|
||||
let safeInset = AppConstants.Defaults.safeInset
|
||||
let availableW = max(1, size.width - safeInset * 2)
|
||||
let availableH = max(1, size.height - safeInset * 2)
|
||||
let availableW = max(1, availableSize.width - safeInset * 2)
|
||||
let availableH = max(1, availableSize.height - safeInset * 2)
|
||||
|
||||
// Calculate scaling factors
|
||||
let widthScale = availableW / max(totalWidth, 1)
|
||||
@ -142,16 +166,16 @@ struct TimeDisplayView: View {
|
||||
Group {
|
||||
if portrait {
|
||||
VStack(spacing: 0) {
|
||||
TimeSegment(text: hour, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity)
|
||||
TimeSegment(text: hour, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign)
|
||||
if showAMPM {
|
||||
TimeSegment(text: ampmText, fontSize: ampmFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity)
|
||||
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)
|
||||
}
|
||||
TimeSegment(text: minute, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity)
|
||||
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)
|
||||
TimeSegment(text: secondsText, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity)
|
||||
TimeSegment(text: secondsText, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign)
|
||||
} else {
|
||||
// Invisible placeholder to maintain consistent spacing
|
||||
Spacer()
|
||||
@ -160,16 +184,16 @@ struct TimeDisplayView: View {
|
||||
}
|
||||
} else {
|
||||
HStack(spacing: 0) {
|
||||
TimeSegment(text: hour, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity)
|
||||
TimeSegment(text: hour, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign)
|
||||
if showAMPM {
|
||||
TimeSegment(text: ampmText, fontSize: ampmFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity)
|
||||
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)
|
||||
}
|
||||
TimeSegment(text: minute, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity)
|
||||
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)
|
||||
TimeSegment(text: secondsText, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity)
|
||||
TimeSegment(text: secondsText, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign)
|
||||
} else {
|
||||
// Invisible placeholder to maintain consistent spacing
|
||||
Spacer()
|
||||
@ -178,7 +202,7 @@ struct TimeDisplayView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(width: size.width, height: size.height, alignment: .center)
|
||||
.frame(width: fullScreenSize.width, height: fullScreenSize.height, alignment: .center)
|
||||
.scaleEffect(finalScale, anchor: .center)
|
||||
.animation(UIConstants.AnimationCurves.smooth, value: finalScale)
|
||||
.animation(UIConstants.AnimationCurves.smooth, value: showSeconds) // Smooth animation for seconds toggle
|
||||
@ -258,20 +282,38 @@ private struct TimeSegment: View {
|
||||
let opacity: Double
|
||||
let digitColor: Color
|
||||
let glowIntensity: Double
|
||||
let fontFamily: String
|
||||
let fontWeight: String
|
||||
let fontDesign: String
|
||||
|
||||
var body: some View {
|
||||
let clamped = ColorUtils.clampOpacity(opacity)
|
||||
let font = UIFont.systemFont(ofSize: fontSize, weight: .bold)
|
||||
let font = FontUtils.customUIFont(
|
||||
size: fontSize,
|
||||
family: fontFamily,
|
||||
weight: fontWeight,
|
||||
design: fontDesign
|
||||
)
|
||||
let maxWidth = FontUtils.maxDigitWidth(font: font)
|
||||
|
||||
ZStack {
|
||||
Text(text)
|
||||
.font(.system(size: fontSize, weight: .bold, design: .rounded))
|
||||
.font(FontUtils.customFont(
|
||||
size: fontSize,
|
||||
family: fontFamily,
|
||||
weight: fontWeight,
|
||||
design: fontDesign
|
||||
))
|
||||
.foregroundColor(digitColor)
|
||||
.blur(radius: ColorUtils.glowRadius(intensity: glowIntensity))
|
||||
.opacity(ColorUtils.glowOpacity(intensity: glowIntensity) * clamped)
|
||||
Text(text)
|
||||
.font(.system(size: fontSize, weight: .bold, design: .rounded))
|
||||
.font(FontUtils.customFont(
|
||||
size: fontSize,
|
||||
family: fontFamily,
|
||||
weight: fontWeight,
|
||||
design: fontDesign
|
||||
))
|
||||
.foregroundColor(digitColor)
|
||||
.opacity(clamped)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user