Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
2d4d76cc8d
commit
d7897d6b38
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
uuid = "20B61D3C-CF97-4427-BF7D-EB393A559DFC"
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
</Bucket>
|
||||
@ -207,6 +207,27 @@ enum FontUtils {
|
||||
)
|
||||
}
|
||||
|
||||
/// 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 weight multiplier for visual consistency with font weight
|
||||
/// - Parameter weight: Font weight name
|
||||
/// - Returns: Multiplier for dot size (0.7 to 1.3)
|
||||
|
||||
@ -113,14 +113,25 @@ struct TimeDisplayView: View {
|
||||
design: fontDesign
|
||||
)
|
||||
|
||||
// Use fixed-width calculations to prevent layout jumping
|
||||
let digitWidth = FontUtils.maxDigitWidth(font: digitUIFont)
|
||||
// Calculate consistent sizes for layout
|
||||
let digitHeight = measureText("8", font: digitUIFont).height // Use 8 as reference height
|
||||
|
||||
// Fixed sizes for consistent layout
|
||||
let hourSize = CGSize(width: digitWidth * 2, height: digitHeight) // Two digits
|
||||
let minuteSize = CGSize(width: digitWidth * 2, height: digitHeight) // Two digits
|
||||
let secondsSize = showSeconds ? CGSize(width: digitWidth * 2, height: digitHeight) : .zero
|
||||
// Calculate the width of "88" for consistent sizing
|
||||
let testFont = FontUtils.customUIFont(
|
||||
size: baseFontSize,
|
||||
family: fontFamily,
|
||||
weight: fontWeight,
|
||||
design: fontDesign
|
||||
)
|
||||
let _ = calculateMaxTextWidth(font: testFont)
|
||||
|
||||
// Calculate width for a single digit (using "8" as the widest)
|
||||
let singleDigitWidth = calculateMaxTextWidth(font: testFont, text: "8")
|
||||
|
||||
// All time segments use the same fixed width to prevent shifting
|
||||
let hourSize = CGSize(width: singleDigitWidth * 2, height: digitHeight)
|
||||
let minuteSize = CGSize(width: singleDigitWidth * 2, height: digitHeight)
|
||||
let secondsSize = showSeconds ? CGSize(width: singleDigitWidth * 2, height: digitHeight) : .zero
|
||||
let ampmSize = showAMPM ? measureText(ampmText, font: ampmUIFont) : .zero
|
||||
|
||||
// Separators - reasonable spacing
|
||||
@ -273,54 +284,104 @@ struct TimeDisplayView: View {
|
||||
}
|
||||
|
||||
// MARK: - Supporting Views
|
||||
private struct TimeSegment: View {
|
||||
let text: String
|
||||
let fontSize: CGFloat
|
||||
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 = FontUtils.customUIFont(
|
||||
size: fontSize,
|
||||
family: fontFamily,
|
||||
weight: fontWeight,
|
||||
design: fontDesign
|
||||
)
|
||||
let maxWidth = FontUtils.maxDigitWidth(font: font)
|
||||
|
||||
ZStack {
|
||||
Text(text)
|
||||
.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(FontUtils.customFont(
|
||||
size: fontSize,
|
||||
family: fontFamily,
|
||||
weight: fontWeight,
|
||||
design: fontDesign
|
||||
))
|
||||
.foregroundColor(digitColor)
|
||||
.opacity(clamped)
|
||||
}
|
||||
.frame(width: maxWidth * CGFloat(text.count), height: nil, alignment: .center)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.lineLimit(1)
|
||||
.allowsTightening(false) // Prevent tightening to maintain fixed width
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
// Calculate width of text for the given font - this ensures consistent width
|
||||
private func calculateMaxTextWidth(font: UIFont, text: String = "88") -> CGFloat {
|
||||
let attributes = [NSAttributedString.Key.font: font]
|
||||
let size = (text as NSString).size(withAttributes: attributes)
|
||||
return size.width
|
||||
}
|
||||
|
||||
private struct TimeSegment: View {
|
||||
let text: String
|
||||
let fontSize: CGFloat
|
||||
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 = FontUtils.customUIFont(
|
||||
size: fontSize,
|
||||
family: fontFamily,
|
||||
weight: fontWeight,
|
||||
design: fontDesign
|
||||
)
|
||||
let singleDigitWidth = calculateMaxTextWidth(font: font, text: "8")
|
||||
let totalWidth = singleDigitWidth * CGFloat(text.count)
|
||||
|
||||
HStack(spacing: 0) {
|
||||
ForEach(Array(text.enumerated()), id: \.offset) { index, character in
|
||||
DigitView(
|
||||
digit: String(character),
|
||||
fontSize: fontSize,
|
||||
opacity: clamped,
|
||||
digitColor: digitColor,
|
||||
glowIntensity: glowIntensity,
|
||||
fontFamily: fontFamily,
|
||||
fontWeight: fontWeight,
|
||||
fontDesign: fontDesign,
|
||||
digitWidth: singleDigitWidth
|
||||
)
|
||||
}
|
||||
}
|
||||
.frame(width: totalWidth, alignment: .center)
|
||||
.border(Color.blue.opacity(0.5), width: 1)
|
||||
}
|
||||
|
||||
// Calculate width of text for the given font - this ensures consistent width
|
||||
private func calculateMaxTextWidth(font: UIFont, text: String = "88") -> CGFloat {
|
||||
let attributes = [NSAttributedString.Key.font: font]
|
||||
let size = (text as NSString).size(withAttributes: attributes)
|
||||
return size.width
|
||||
}
|
||||
}
|
||||
|
||||
private struct DigitView: View {
|
||||
let digit: String
|
||||
let fontSize: CGFloat
|
||||
let opacity: Double
|
||||
let digitColor: Color
|
||||
let glowIntensity: Double
|
||||
let fontFamily: String
|
||||
let fontWeight: String
|
||||
let fontDesign: String
|
||||
let digitWidth: CGFloat
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Text(digit)
|
||||
.font(FontUtils.customFont(
|
||||
size: fontSize,
|
||||
family: fontFamily,
|
||||
weight: fontWeight,
|
||||
design: fontDesign
|
||||
))
|
||||
.foregroundColor(digitColor)
|
||||
.blur(radius: ColorUtils.glowRadius(intensity: glowIntensity))
|
||||
.opacity(ColorUtils.glowOpacity(intensity: glowIntensity) * opacity)
|
||||
.multilineTextAlignment(.center)
|
||||
Text(digit)
|
||||
.font(FontUtils.customFont(
|
||||
size: fontSize,
|
||||
family: fontFamily,
|
||||
weight: fontWeight,
|
||||
design: fontDesign
|
||||
))
|
||||
.foregroundColor(digitColor)
|
||||
.opacity(opacity)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.frame(width: digitWidth, alignment: .center)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.lineLimit(1)
|
||||
.allowsTightening(false)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct HorizontalColon: View {
|
||||
let dotDiameter: CGFloat
|
||||
|
||||
Loading…
Reference in New Issue
Block a user