Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
Matt Bruce 2025-09-08 10:41:51 -05:00
parent c1128ac87a
commit 2fafae909d
8 changed files with 413 additions and 53 deletions

12
PRD.md
View File

@ -11,8 +11,10 @@ TheNoiseClock is a SwiftUI-based iOS application that combines a customizable di
- **Optional seconds display** with toggle control
- **AM/PM badge** for 12-hour format (optional)
- **Segmented time display** with colon separators that adapt to orientation
- **Dynamic scaling** that fits available screen space
- **Portrait and landscape orientation support**
- **Dynamic scaling** that maximizes available screen space usage
- **Portrait and landscape orientation support** with responsive font sizing
- **Optimal font sizing** that uses all available space efficiently
- **Immediate updates** on orientation changes and tab bar visibility changes
### 2. Clock Customization
- **Color customization**: User-selectable digit colors with color picker
@ -50,7 +52,7 @@ TheNoiseClock is a SwiftUI-based iOS application that combines a customizable di
### 6. Advanced Alarm System
- **Multiple alarms**: Create and manage unlimited alarms
- **Rich alarm editor**: Full-featured alarm creation and editing interface
- **Time selection**: Wheel-style date picker for precise alarm time
- **Time selection**: Wheel-style date picker with optimized font sizing for maximum readability
- **Dynamic alarm sounds**: Configurable alarm sounds loaded from JSON configuration
- **Sound preview**: Play/stop functionality for testing alarm sounds before selection
- **Sound organization**: Alarm sounds organized in bundles with categories
@ -66,6 +68,7 @@ TheNoiseClock is a SwiftUI-based iOS application that combines a customizable di
- **Persistent storage**: Alarms saved to UserDefaults with backward compatibility
- **Alarm management**: Add, edit, delete, and duplicate alarms
- **Next trigger preview**: Shows when the next alarm will fire
- **Responsive time picker**: Font sizes adapt to available space and orientation
## Technical Architecture
@ -194,9 +197,10 @@ TheNoiseClock/
│ ├── Extensions/
│ │ ├── Color+Extensions.swift # Color utilities and extensions
│ │ ├── Date+Extensions.swift # Date formatting and utilities
│ │ └── View+Extensions.swift # Common view modifiers
│ │ └── View+Extensions.swift # Common view modifiers and responsive utilities
│ └── Utilities/
│ ├── ColorUtils.swift # Color manipulation utilities
│ ├── FontUtils.swift # Font sizing and typography utilities
│ └── NotificationUtils.swift # Notification helper functions
├── Models/
│ ├── ClockStyle.swift # Clock customization data model

View File

@ -403,8 +403,7 @@
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -435,8 +434,7 @@
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",

View File

@ -54,7 +54,7 @@ enum AppConstants {
static let clockOpacity = 0.5
static let overlayOpacity = 0.5
static let maxFontSize = 220.0
static let safeInset = 8.0
static let safeInset = 4.0 // Reasonable safe inset
}
// MARK: - System Sounds

View File

@ -56,6 +56,64 @@ extension View {
tabBar.isUserInteractionEnabled = !hidden
#endif
}
/// Apply responsive font sizing that updates on orientation and layout changes
/// - Parameters:
/// - baseSize: Base font size
/// - isPortrait: Whether in portrait orientation
/// - showSeconds: Whether seconds are displayed (for time components)
/// - showAmPm: Whether AM/PM is displayed
/// - Returns: View with responsive font sizing
func responsiveFontSize(
baseSize: CGFloat,
isPortrait: Bool,
showSeconds: Bool = false,
showAmPm: Bool = false
) -> some View {
self.modifier(ResponsiveFontModifier(
baseSize: baseSize,
isPortrait: isPortrait,
showSeconds: showSeconds,
showAmPm: showAmPm
))
}
/// Force view to update on orientation changes
/// - Returns: View that updates on orientation changes
func onOrientationChange() -> some View {
self.modifier(OrientationChangeModifier())
}
}
// MARK: - View Modifiers
/// Modifier for responsive font sizing that updates on layout changes
struct ResponsiveFontModifier: ViewModifier {
let baseSize: CGFloat
let isPortrait: Bool
let showSeconds: Bool
let showAmPm: Bool
func body(content: Content) -> some View {
content
.font(.system(size: baseSize, weight: .bold, design: .rounded))
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
// Force view update on orientation change
}
}
}
/// Modifier that forces view updates on orientation changes
struct OrientationChangeModifier: ViewModifier {
@State private var orientation = UIDevice.current.orientation
func body(content: Content) -> some View {
content
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
orientation = UIDevice.current.orientation
}
.id(orientation.rawValue) // Force view recreation on orientation change
}
}
#if canImport(UIKit)

View File

@ -31,20 +31,4 @@ enum ColorUtils {
return max(0.0, min(opacity, 1.0))
}
/// Calculate dynamic font size based on container dimensions
/// - Parameters:
/// - containerWidth: Container width
/// - containerHeight: Container height
/// - Returns: Calculated font size
static func dynamicFontSize(containerWidth: CGFloat, containerHeight: CGFloat) -> CGFloat {
let shortest = min(containerWidth, containerHeight)
return min(shortest * 0.28, AppConstants.Defaults.maxFontSize)
}
/// Calculate AM/PM font size based on base font size
/// - Parameter baseFontSize: Base font size
/// - Returns: AM/PM font size (20% of base)
static func ampmFontSize(baseFontSize: CGFloat) -> CGFloat {
return baseFontSize * 0.20
}
}

View File

@ -0,0 +1,252 @@
//
// FontUtils.swift
// TheNoiseClock
//
// Created by Matt Bruce on 9/7/25.
//
import SwiftUI
/// Font sizing and typography utilities
enum FontUtils {
/// Calculate dynamic font size based on container dimensions
/// - Parameters:
/// - containerWidth: Container width
/// - containerHeight: Container height
/// - Returns: Calculated font size
static func dynamicFontSize(containerWidth: CGFloat, containerHeight: CGFloat) -> CGFloat {
let shortest = min(containerWidth, containerHeight)
return min(shortest * 0.28, AppConstants.Defaults.maxFontSize)
}
/// Calculate optimal font size that maximizes space usage
/// - Parameters:
/// - containerWidth: Available container width
/// - containerHeight: Available container height
/// - isPortrait: Whether the device is in portrait orientation
/// - showSeconds: Whether seconds are displayed
/// - showAmPm: Whether AM/PM is displayed
/// - Returns: Optimal font size for maximum space utilization
static func optimalFontSize(
containerWidth: CGFloat,
containerHeight: CGFloat,
isPortrait: Bool,
showSeconds: Bool = false,
showAmPm: Bool = false
) -> CGFloat {
// Account for safe areas and padding
let safeInset = AppConstants.Defaults.safeInset
let availableWidth = max(1, containerWidth - safeInset * 2)
let availableHeight = max(1, containerHeight - safeInset * 2)
// Estimate text content requirements (for future use)
_ = showSeconds ? 6 : 4 // HH:MM or HH:MM:SS
_ = showAmPm ? 2 : 0 // AM/PM
// Calculate optimal size based on orientation and content
let optimalSize: CGFloat
if isPortrait {
// In portrait, height is the limiting factor
// Account for separators and spacing
let separatorHeight = availableHeight * 0.08 // 8% for separators (reduced)
let contentHeight = availableHeight - separatorHeight
let estimatedLines = showSeconds ? 3 : 2 // HH, MM, SS or HH, MM
let lineHeight = contentHeight / CGFloat(estimatedLines)
optimalSize = lineHeight * 0.85 // 85% of line height for actual text (increased)
} else {
// In landscape, be more aggressive with space usage
// Account for separators and spacing
let separatorWidth = availableWidth * 0.08 // 8% for separators (reduced)
let contentWidth = availableWidth - separatorWidth
let estimatedColumns = showSeconds ? 3 : 2 // HH, MM, SS or HH, MM
let columnWidth = contentWidth / CGFloat(estimatedColumns)
optimalSize = columnWidth * 0.75 // 75% of column width for actual text (increased)
}
// Apply reasonable bounds
let minSize: CGFloat = 20
let maxSize: CGFloat = AppConstants.Defaults.maxFontSize
return max(minSize, min(optimalSize, maxSize))
}
/// Calculate font size that fills available space with scaling
/// - Parameters:
/// - containerWidth: Available container width
/// - containerHeight: Available container height
/// - textContent: The actual text content to measure
/// - isPortrait: Whether the device is in portrait orientation
/// - Returns: Font size that maximizes space usage
static func fillSpaceFontSize(
containerWidth: CGFloat,
containerHeight: CGFloat,
textContent: String,
isPortrait: Bool
) -> CGFloat {
let safeInset = AppConstants.Defaults.safeInset
let availableWidth = max(1, containerWidth - safeInset * 2)
let availableHeight = max(1, containerHeight - safeInset * 2)
// Start with a reasonable base size
let baseSize = isPortrait ? availableHeight * 0.3 : availableWidth * 0.15
// Binary search for optimal size
var low: CGFloat = 10
var high: CGFloat = AppConstants.Defaults.maxFontSize
var bestSize: CGFloat = baseSize
for _ in 0..<10 { // Limit iterations
let testSize = (low + high) / 2
let font = UIFont.systemFont(ofSize: testSize, weight: .bold)
let textSize = measureTextSize(text: textContent, font: font)
let fitsWidth = textSize.width <= availableWidth
let fitsHeight = textSize.height <= availableHeight
if fitsWidth && fitsHeight {
bestSize = testSize
low = testSize
} else {
high = testSize
}
}
return bestSize
}
/// Calculate AM/PM font size based on base font size
/// - Parameter baseFontSize: Base font size
/// - Returns: AM/PM font size (20% of base)
static func ampmFontSize(baseFontSize: CGFloat) -> CGFloat {
return baseFontSize * 0.20
}
/// Measure text size with given font
/// - Parameters:
/// - text: Text to measure
/// - font: Font to use for measurement
/// - Returns: Size of the text
static func measureTextSize(text: String, font: UIFont) -> CGSize {
let attributes = [NSAttributedString.Key.font: font]
return (text as NSString).size(withAttributes: attributes)
}
/// Get the maximum character width for digits to ensure consistent spacing
/// - Parameter font: The font to measure with
/// - Returns: The width of the widest digit character
static func maxDigitWidth(font: UIFont) -> CGFloat {
let digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
var maxWidth: CGFloat = 0
for digit in digits {
let size = measureTextSize(text: digit, font: font)
maxWidth = max(maxWidth, size.width)
}
return maxWidth
}
/// Calculate fixed-width layout size for time display to prevent jumping
/// - Parameters:
/// - font: The font to use for measurements
/// - showSeconds: Whether seconds are displayed
/// - showAmPm: Whether AM/PM is displayed
/// - isPortrait: Whether the device is in portrait orientation
/// - Returns: Total width and height needed for the layout
static func fixedWidthLayoutSize(
font: UIFont,
showSeconds: Bool,
showAmPm: Bool,
isPortrait: Bool
) -> (width: CGFloat, height: CGFloat) {
let digitWidth = maxDigitWidth(font: font)
let digitHeight = measureTextSize(text: "8", font: font).height // Use 8 as reference height
// Calculate separator sizes
let dotDiameter = font.pointSize * 0.20
let hSpacing = font.pointSize * 0.18
let vSpacing = font.pointSize * 0.22
if isPortrait {
// Portrait: vertical layout
let totalWidth = digitWidth * 2 // Two digits per row
let separatorHeight = dotDiameter * 2 + vSpacing
let contentHeight = digitHeight * (showSeconds ? 3 : 2) + separatorHeight
return (totalWidth, contentHeight)
} else {
// Landscape: horizontal layout
let digitCount = showSeconds ? 6 : 4 // HH:MM or HH:MM:SS
let separatorCount = showSeconds ? 2 : 1
let separatorWidth = dotDiameter * 2 + hSpacing
let totalWidth = digitWidth * CGFloat(digitCount) + separatorWidth * CGFloat(separatorCount)
let totalHeight = digitHeight
return (totalWidth, totalHeight)
}
}
/// Calculate responsive font size for time picker components
/// - Parameters:
/// - containerWidth: Available container width
/// - containerHeight: Available container height
/// - isPortrait: Whether the device is in portrait orientation
/// - Returns: Font size optimized for time picker
static func timePickerFontSize(
containerWidth: CGFloat,
containerHeight: CGFloat,
isPortrait: Bool
) -> CGFloat {
let safeInset = AppConstants.Defaults.safeInset
let availableWidth = max(1, containerWidth - safeInset * 2)
let availableHeight = max(1, containerHeight - safeInset * 2)
// For time picker, we want larger, more readable fonts
let baseSize = isPortrait ? availableHeight * 0.15 : availableWidth * 0.08
// Apply bounds with higher minimum for readability
let minSize: CGFloat = 24
let maxSize: CGFloat = 72
return max(minSize, min(baseSize, maxSize))
}
/// Calculate maximum font size for stretched mode that uses visible space without overflow
/// - Parameters:
/// - containerWidth: Available container width
/// - containerHeight: Available container height
/// - isPortrait: Whether the device is in portrait orientation
/// - showSeconds: Whether seconds are displayed
/// - showAmPm: Whether AM/PM is displayed
/// - Returns: Maximum font size that fits within visible space
static func maximumStretchedFontSize(
containerWidth: CGFloat,
containerHeight: CGFloat,
isPortrait: Bool,
showSeconds: Bool = false,
showAmPm: Bool = false
) -> CGFloat {
// Use reasonable safe areas
let safeInset = AppConstants.Defaults.safeInset
let availableWidth = max(1, containerWidth - safeInset * 2)
let availableHeight = max(1, containerHeight - safeInset * 2)
// Calculate optimal size with reasonable space usage
let optimalSize: CGFloat
if isPortrait {
// In portrait, use most of the available height
let contentHeight = availableHeight * 0.85 // Use 85% of available height
let estimatedLines = showSeconds ? 3 : 2
let lineHeight = contentHeight / CGFloat(estimatedLines)
optimalSize = lineHeight * 0.8 // Use 80% of line height
} else {
// In landscape, use most of the available width
let contentWidth = availableWidth * 0.85 // Use 85% of available width
let estimatedColumns = showSeconds ? 3 : 2
let columnWidth = contentWidth / CGFloat(estimatedColumns)
optimalSize = columnWidth * 0.7 // Use 70% of column width
}
// Apply reasonable bounds
let minSize: CGFloat = 20
let maxSize: CGFloat = AppConstants.Defaults.maxFontSize
return max(minSize, min(optimalSize, maxSize))
}
}

View File

@ -7,21 +7,37 @@
import SwiftUI
/// Time picker component for alarm creation
/// Time picker component for alarm creation with optimized font sizing
struct TimePickerSection: View {
@Binding var selectedTime: Date
var body: some View {
VStack(spacing: 0) {
DatePicker(
"Time",
selection: $selectedTime,
displayedComponents: .hourAndMinute
GeometryReader { proxy in
let size = proxy.size
let portrait = size.height >= size.width
// Calculate optimal font size for time picker
let pickerFontSize = FontUtils.timePickerFontSize(
containerWidth: size.width,
containerHeight: size.height,
isPortrait: portrait
)
.datePickerStyle(.wheel)
.labelsHidden()
.frame(height: 200)
VStack(spacing: 0) {
DatePicker(
"Time",
selection: $selectedTime,
displayedComponents: .hourAndMinute
)
.datePickerStyle(.wheel)
.labelsHidden()
.font(.system(size: pickerFontSize, weight: .medium, design: .rounded))
.frame(maxWidth: .infinity, maxHeight: .infinity)
.clipped()
}
.background(Color(.systemGroupedBackground))
}
.background(Color(.systemGroupedBackground))
.frame(height: 200)
.onOrientationChange() // Force updates on orientation changes
}
}

View File

@ -62,8 +62,24 @@ struct TimeDisplayView: View {
GeometryReader { proxy in
let size = proxy.size
let portrait = size.height >= size.width
let baseFontSize = ColorUtils.dynamicFontSize(containerWidth: size.width, containerHeight: size.height)
let ampmFontSize = ColorUtils.ampmFontSize(baseFontSize: baseFontSize)
// Use optimal font sizing that maximizes space usage
let baseFontSize = stretched ?
FontUtils.maximumStretchedFontSize(
containerWidth: size.width,
containerHeight: size.height,
isPortrait: portrait,
showSeconds: showSeconds,
showAmPm: !use24Hour && showAmPmBadge
) :
FontUtils.optimalFontSize(
containerWidth: size.width,
containerHeight: size.height,
isPortrait: portrait,
showSeconds: showSeconds,
showAmPm: !use24Hour && showAmPmBadge
)
let ampmFontSize = FontUtils.ampmFontSize(baseFontSize: baseFontSize)
// Time components
let hour = use24Hour ? Self.hour24DF.string(from: date) : Self.hour12DF.string(from: date)
@ -72,15 +88,21 @@ struct TimeDisplayView: View {
let ampmText = Self.ampmDF.string(from: date)
let showAMPM = !use24Hour && showAmPmBadge
// Calculate sizes
// 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 hourSize = measureText(hour, font: digitUIFont)
let minuteSize = measureText(minute, font: digitUIFont)
let secondsSize = showSeconds ? measureText(secondsText, font: digitUIFont) : .zero
// Use fixed-width calculations to prevent layout jumping
let digitWidth = FontUtils.maxDigitWidth(font: digitUIFont)
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
let ampmSize = showAMPM ? measureText(ampmText, font: ampmUIFont) : .zero
// Separators
// Separators - reasonable spacing
let dotDiameter = baseFontSize * 0.20
let hSpacing = baseFontSize * 0.18
let vSpacing = baseFontSize * 0.22
@ -100,17 +122,23 @@ struct TimeDisplayView: View {
showAMPM: showAMPM
)
// Calculate scale
// Calculate scale with maximum space utilization
let safeInset = AppConstants.Defaults.safeInset
let availableW = max(1, size.width - safeInset * 2)
let availableH = max(1, size.height - safeInset * 2)
// Calculate scaling factors
let widthScale = availableW / max(totalWidth, 1)
let heightScale = availableH / max(totalHeight, 1)
let fittedScale = max(0.1, min(widthScale, heightScale))
let manualPercent = max(0.0, min(manualScale, 1.0))
let effectiveScale = stretched ? fittedScale : max(0.05, fittedScale * CGFloat(manualPercent))
// Time display
// For stretched mode, use reasonable scaling
let effectiveScale = stretched ?
max(0.1, min(min(widthScale, heightScale), 1.5)) : // Use min scale and cap at 1.5x to prevent overflow
max(0.1, max(0.1, min(widthScale, heightScale)) * CGFloat(max(0.1, min(manualScale, 1.0))))
let finalScale = effectiveScale
// Time display with consistent centering and stable layout
Group {
if portrait {
VStack(spacing: 0) {
@ -124,6 +152,10 @@ struct TimeDisplayView: View {
if showSeconds {
HorizontalColon(dotDiameter: dotDiameter, spacing: hSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity)
TimeSegment(text: secondsText, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity)
} else {
// Invisible placeholder to maintain consistent spacing
Spacer()
.frame(height: baseFontSize * 0.3)
}
}
} else {
@ -138,22 +170,34 @@ struct TimeDisplayView: View {
if showSeconds {
VerticalColon(dotDiameter: dotDiameter, spacing: vSpacing, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity)
TimeSegment(text: secondsText, fontSize: baseFontSize, opacity: clockOpacity, digitColor: digitColor, glowIntensity: glowIntensity)
} else {
// Invisible placeholder to maintain consistent spacing
Spacer()
.frame(width: baseFontSize * 0.3)
}
}
}
}
.frame(width: size.width, height: size.height, alignment: .center)
.scaleEffect(effectiveScale, anchor: .center)
.animation(UIConstants.AnimationCurves.smooth, value: effectiveScale)
.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
.overlay(
// Debug border to visualize actual bounds
Rectangle()
.stroke(Color.red, lineWidth: 2)
.opacity(0.5)
)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.onOrientationChange() // Force updates on orientation changes
}
// MARK: - Helper Methods
private func measureText(_ text: String, font: UIFont) -> CGSize {
let attributes = [NSAttributedString.Key.font: font]
return (text as NSString).size(withAttributes: attributes)
return FontUtils.measureTextSize(text: text, font: font)
}
private func calculateLayoutSize(
@ -217,6 +261,9 @@ private struct TimeSegment: View {
var body: some View {
let clamped = ColorUtils.clampOpacity(opacity)
let font = UIFont.systemFont(ofSize: fontSize, weight: .bold)
let maxWidth = FontUtils.maxDigitWidth(font: font)
ZStack {
Text(text)
.font(.system(size: fontSize, weight: .bold, design: .rounded))
@ -228,9 +275,10 @@ private struct TimeSegment: View {
.foregroundColor(digitColor)
.opacity(clamped)
}
.fixedSize(horizontal: true, vertical: true)
.frame(width: maxWidth * CGFloat(text.count), height: nil, alignment: .center)
.fixedSize(horizontal: false, vertical: true)
.lineLimit(1)
.allowsTightening(true)
.allowsTightening(false) // Prevent tightening to maintain fixed width
.multilineTextAlignment(.center)
}
}