Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
c8fa702382
commit
2d4d76cc8d
@ -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
|
/// Create a UIFont with specified parameters for measurements
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - size: Font size
|
/// - size: Font size
|
||||||
|
|||||||
@ -22,7 +22,7 @@ struct ClockView: View {
|
|||||||
|
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
ZStack {
|
ZStack {
|
||||||
// Time display - fills available space within safe areas
|
// Time display - fills entire available space
|
||||||
TimeDisplayView(
|
TimeDisplayView(
|
||||||
date: viewModel.currentTime,
|
date: viewModel.currentTime,
|
||||||
use24Hour: viewModel.style.use24Hour,
|
use24Hour: viewModel.style.use24Hour,
|
||||||
@ -37,7 +37,6 @@ struct ClockView: View {
|
|||||||
fontWeight: viewModel.style.fontWeight,
|
fontWeight: viewModel.style.fontWeight,
|
||||||
fontDesign: viewModel.style.fontDesign
|
fontDesign: viewModel.style.fontDesign
|
||||||
)
|
)
|
||||||
.frame(width: geometry.size.width, height: geometry.size.height)
|
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
|
|
||||||
// Top overlay - positioned at top with safe area consideration
|
// Top overlay - positioned at top with safe area consideration
|
||||||
@ -60,6 +59,7 @@ struct ClockView: View {
|
|||||||
.animation(UIConstants.AnimationCurves.smooth, value: viewModel.isDisplayMode)
|
.animation(UIConstants.AnimationCurves.smooth, value: viewModel.isDisplayMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.ignoresSafeArea(.all, edges: viewModel.isDisplayMode ? .bottom : [])
|
||||||
.navigationTitle(viewModel.isDisplayMode ? "" : "Clock")
|
.navigationTitle(viewModel.isDisplayMode ? "" : "Clock")
|
||||||
.toolbar {
|
.toolbar {
|
||||||
if !viewModel.isDisplayMode {
|
if !viewModel.isDisplayMode {
|
||||||
|
|||||||
@ -66,29 +66,26 @@ struct TimeDisplayView: View {
|
|||||||
let size = proxy.size
|
let size = proxy.size
|
||||||
let portrait = size.height >= size.width
|
let portrait = size.height >= size.width
|
||||||
|
|
||||||
// Get safe area information to avoid Dynamic Island overlap
|
// Use the full GeometryReader size for maximum space usage
|
||||||
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
|
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 ?
|
let baseFontSize = stretched ?
|
||||||
FontUtils.maximumStretchedFontSize(
|
FontUtils.maximumStretchedFontSize(
|
||||||
containerWidth: availableSize.width,
|
containerWidth: fullScreenSize.width,
|
||||||
containerHeight: availableSize.height,
|
containerHeight: fullScreenSize.height,
|
||||||
isPortrait: portrait,
|
isPortrait: portrait,
|
||||||
showSeconds: showSeconds,
|
showSeconds: showSeconds,
|
||||||
showAmPm: !use24Hour && showAmPmBadge
|
showAmPm: !use24Hour && showAmPmBadge
|
||||||
) :
|
) :
|
||||||
FontUtils.optimalFontSize(
|
FontUtils.optimalFontSize(
|
||||||
containerWidth: availableSize.width,
|
containerWidth: fullScreenSize.width,
|
||||||
containerHeight: availableSize.height,
|
containerHeight: fullScreenSize.height,
|
||||||
isPortrait: portrait,
|
isPortrait: portrait,
|
||||||
showSeconds: showSeconds,
|
showSeconds: showSeconds,
|
||||||
showAmPm: !use24Hour && showAmPmBadge
|
showAmPm: !use24Hour && showAmPmBadge
|
||||||
@ -146,10 +143,10 @@ struct TimeDisplayView: View {
|
|||||||
showAMPM: showAMPM
|
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 safeInset = AppConstants.Defaults.safeInset
|
||||||
let availableW = max(1, availableSize.width - safeInset * 2)
|
let availableW = max(1, fullScreenSize.width - safeInset * 2)
|
||||||
let availableH = max(1, availableSize.height - safeInset * 2)
|
let availableH = max(1, fullScreenSize.height - safeInset * 2)
|
||||||
|
|
||||||
// Calculate scaling factors
|
// Calculate scaling factors
|
||||||
let widthScale = availableW / max(totalWidth, 1)
|
let widthScale = availableW / max(totalWidth, 1)
|
||||||
@ -170,11 +167,11 @@ struct TimeDisplayView: View {
|
|||||||
if showAMPM {
|
if showAMPM {
|
||||||
TimeSegment(text: ampmText, fontSize: ampmFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign)
|
TimeSegment(text: ampmText, fontSize: ampmFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign)
|
||||||
} else {
|
} 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)
|
TimeSegment(text: minute, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign)
|
||||||
if showSeconds {
|
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)
|
TimeSegment(text: secondsText, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign)
|
||||||
} else {
|
} else {
|
||||||
// Invisible placeholder to maintain consistent spacing
|
// Invisible placeholder to maintain consistent spacing
|
||||||
@ -188,11 +185,11 @@ struct TimeDisplayView: View {
|
|||||||
if showAMPM {
|
if showAMPM {
|
||||||
TimeSegment(text: ampmText, fontSize: ampmFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign)
|
TimeSegment(text: ampmText, fontSize: ampmFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign)
|
||||||
} else {
|
} 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)
|
TimeSegment(text: minute, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign)
|
||||||
if showSeconds {
|
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)
|
TimeSegment(text: secondsText, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity, fontFamily: fontFamily, fontWeight: fontWeight, fontDesign: fontDesign)
|
||||||
} else {
|
} else {
|
||||||
// Invisible placeholder to maintain consistent spacing
|
// Invisible placeholder to maintain consistent spacing
|
||||||
@ -331,12 +328,13 @@ private struct HorizontalColon: View {
|
|||||||
let opacity: Double
|
let opacity: Double
|
||||||
let digitColor: Color
|
let digitColor: Color
|
||||||
let glowIntensity: Double
|
let glowIntensity: Double
|
||||||
|
let fontWeight: String
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let clamped = ColorUtils.clampOpacity(opacity)
|
let clamped = ColorUtils.clampOpacity(opacity)
|
||||||
HStack(spacing: spacing) {
|
HStack(spacing: spacing) {
|
||||||
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)
|
DotCircle(size: dotDiameter, opacity: clamped, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight)
|
||||||
}
|
}
|
||||||
.fixedSize(horizontal: true, vertical: true)
|
.fixedSize(horizontal: true, vertical: true)
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
@ -349,12 +347,13 @@ private struct VerticalColon: View {
|
|||||||
let opacity: Double
|
let opacity: Double
|
||||||
let digitColor: Color
|
let digitColor: Color
|
||||||
let glowIntensity: Double
|
let glowIntensity: Double
|
||||||
|
let fontWeight: String
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let clamped = ColorUtils.clampOpacity(opacity)
|
let clamped = ColorUtils.clampOpacity(opacity)
|
||||||
VStack(spacing: spacing) {
|
VStack(spacing: spacing) {
|
||||||
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)
|
DotCircle(size: dotDiameter, opacity: clamped, digitColor: digitColor, glowIntensity: glowIntensity, fontWeight: fontWeight)
|
||||||
}
|
}
|
||||||
.fixedSize(horizontal: true, vertical: true)
|
.fixedSize(horizontal: true, vertical: true)
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
@ -366,17 +365,21 @@ private struct DotCircle: View {
|
|||||||
let opacity: Double
|
let opacity: Double
|
||||||
let digitColor: Color
|
let digitColor: Color
|
||||||
let glowIntensity: Double
|
let glowIntensity: Double
|
||||||
|
let fontWeight: String
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
let weightMultiplier = FontUtils.weightMultiplier(for: fontWeight)
|
||||||
|
let adjustedSize = size * weightMultiplier
|
||||||
|
|
||||||
ZStack {
|
ZStack {
|
||||||
Circle()
|
Circle()
|
||||||
.fill(digitColor)
|
.fill(digitColor)
|
||||||
.frame(width: size, height: size)
|
.frame(width: adjustedSize, height: adjustedSize)
|
||||||
.blur(radius: ColorUtils.glowRadius(intensity: glowIntensity))
|
.blur(radius: ColorUtils.glowRadius(intensity: glowIntensity))
|
||||||
.opacity(ColorUtils.glowOpacity(intensity: glowIntensity) * opacity)
|
.opacity(ColorUtils.glowOpacity(intensity: glowIntensity) * opacity)
|
||||||
Circle()
|
Circle()
|
||||||
.fill(digitColor)
|
.fill(digitColor)
|
||||||
.frame(width: size, height: size)
|
.frame(width: adjustedSize, height: adjustedSize)
|
||||||
.opacity(opacity)
|
.opacity(opacity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user