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
|
/// Get weight multiplier for visual consistency with font weight
|
||||||
/// - Parameter weight: Font weight name
|
/// - Parameter weight: Font weight name
|
||||||
/// - Returns: Multiplier for dot size (0.7 to 1.3)
|
/// - Returns: Multiplier for dot size (0.7 to 1.3)
|
||||||
|
|||||||
@ -113,14 +113,25 @@ struct TimeDisplayView: View {
|
|||||||
design: fontDesign
|
design: fontDesign
|
||||||
)
|
)
|
||||||
|
|
||||||
// Use fixed-width calculations to prevent layout jumping
|
// Calculate consistent sizes for layout
|
||||||
let digitWidth = FontUtils.maxDigitWidth(font: digitUIFont)
|
|
||||||
let digitHeight = measureText("8", font: digitUIFont).height // Use 8 as reference height
|
let digitHeight = measureText("8", font: digitUIFont).height // Use 8 as reference height
|
||||||
|
|
||||||
// Fixed sizes for consistent layout
|
// Calculate the width of "88" for consistent sizing
|
||||||
let hourSize = CGSize(width: digitWidth * 2, height: digitHeight) // Two digits
|
let testFont = FontUtils.customUIFont(
|
||||||
let minuteSize = CGSize(width: digitWidth * 2, height: digitHeight) // Two digits
|
size: baseFontSize,
|
||||||
let secondsSize = showSeconds ? CGSize(width: digitWidth * 2, height: digitHeight) : .zero
|
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
|
let ampmSize = showAMPM ? measureText(ampmText, font: ampmUIFont) : .zero
|
||||||
|
|
||||||
// Separators - reasonable spacing
|
// Separators - reasonable spacing
|
||||||
@ -273,54 +284,104 @@ struct TimeDisplayView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Supporting Views
|
// MARK: - Supporting Views
|
||||||
private struct TimeSegment: View {
|
|
||||||
let text: String
|
// Calculate width of text for the given font - this ensures consistent width
|
||||||
let fontSize: CGFloat
|
private func calculateMaxTextWidth(font: UIFont, text: String = "88") -> CGFloat {
|
||||||
let opacity: Double
|
let attributes = [NSAttributedString.Key.font: font]
|
||||||
let digitColor: Color
|
let size = (text as NSString).size(withAttributes: attributes)
|
||||||
let glowIntensity: Double
|
return size.width
|
||||||
let fontFamily: String
|
}
|
||||||
let fontWeight: String
|
|
||||||
let fontDesign: String
|
private struct TimeSegment: View {
|
||||||
|
let text: String
|
||||||
var body: some View {
|
let fontSize: CGFloat
|
||||||
let clamped = ColorUtils.clampOpacity(opacity)
|
let opacity: Double
|
||||||
let font = FontUtils.customUIFont(
|
let digitColor: Color
|
||||||
size: fontSize,
|
let glowIntensity: Double
|
||||||
family: fontFamily,
|
let fontFamily: String
|
||||||
weight: fontWeight,
|
let fontWeight: String
|
||||||
design: fontDesign
|
let fontDesign: String
|
||||||
)
|
|
||||||
let maxWidth = FontUtils.maxDigitWidth(font: font)
|
var body: some View {
|
||||||
|
let clamped = ColorUtils.clampOpacity(opacity)
|
||||||
ZStack {
|
let font = FontUtils.customUIFont(
|
||||||
Text(text)
|
size: fontSize,
|
||||||
.font(FontUtils.customFont(
|
family: fontFamily,
|
||||||
size: fontSize,
|
weight: fontWeight,
|
||||||
family: fontFamily,
|
design: fontDesign
|
||||||
weight: fontWeight,
|
)
|
||||||
design: fontDesign
|
let singleDigitWidth = calculateMaxTextWidth(font: font, text: "8")
|
||||||
))
|
let totalWidth = singleDigitWidth * CGFloat(text.count)
|
||||||
.foregroundColor(digitColor)
|
|
||||||
.blur(radius: ColorUtils.glowRadius(intensity: glowIntensity))
|
HStack(spacing: 0) {
|
||||||
.opacity(ColorUtils.glowOpacity(intensity: glowIntensity) * clamped)
|
ForEach(Array(text.enumerated()), id: \.offset) { index, character in
|
||||||
Text(text)
|
DigitView(
|
||||||
.font(FontUtils.customFont(
|
digit: String(character),
|
||||||
size: fontSize,
|
fontSize: fontSize,
|
||||||
family: fontFamily,
|
opacity: clamped,
|
||||||
weight: fontWeight,
|
digitColor: digitColor,
|
||||||
design: fontDesign
|
glowIntensity: glowIntensity,
|
||||||
))
|
fontFamily: fontFamily,
|
||||||
.foregroundColor(digitColor)
|
fontWeight: fontWeight,
|
||||||
.opacity(clamped)
|
fontDesign: fontDesign,
|
||||||
}
|
digitWidth: singleDigitWidth
|
||||||
.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
|
.frame(width: totalWidth, alignment: .center)
|
||||||
.multilineTextAlignment(.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 {
|
private struct HorizontalColon: View {
|
||||||
let dotDiameter: CGFloat
|
let dotDiameter: CGFloat
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user