Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2025-09-08 12:07:17 -05:00
parent 687ca8cca4
commit 3190d610f3
6 changed files with 246 additions and 83 deletions

View File

@ -22,104 +22,43 @@ struct ClockView: View {
GeometryReader { geometry in
ZStack {
// Time display - fills entire available space
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
// Main clock display container
ClockDisplayContainer(
currentTime: viewModel.currentTime,
style: viewModel.style,
isDisplayMode: viewModel.isDisplayMode
)
.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)
// Top overlay container
ClockOverlayContainer(style: viewModel.style)
}
Spacer()
}
}
.scaleEffect(viewModel.isDisplayMode ? 1.0 : 0.995)
.animation(UIConstants.AnimationCurves.smooth, value: viewModel.isDisplayMode)
}
}
.ignoresSafeArea(.all, edges: viewModel.isDisplayMode ? .bottom : [])
.navigationTitle(viewModel.isDisplayMode ? "" : "Clock")
.toolbar {
if !viewModel.isDisplayMode {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
showSettings = true
} label: {
Image(systemName: "gear")
.font(.title2)
.transition(.opacity)
}
}
}
}
.navigationBarBackButtonHidden(viewModel.isDisplayMode)
.toolbar(viewModel.isDisplayMode ? .hidden : .automatic)
.statusBarHidden(viewModel.isDisplayMode)
.onAppear {
setTabBarHidden(viewModel.isDisplayMode, animated: false)
}
.onDisappear {
setTabBarHidden(false, animated: false)
}
.sheet(isPresented: $showSettings) {
ClockSettingsView(style: viewModel.style) { newStyle in
viewModel.updateStyle(newStyle)
}
.presentationDetents([.medium, .large])
}
.contentShape(Rectangle())
.simultaneousGesture(
LongPressGesture(minimumDuration: AppConstants.DisplayMode.longPressDuration)
.onEnded { _ in
viewModel.toggleDisplayMode()
setTabBarHidden(viewModel.isDisplayMode, animated: true)
// Status bar hiding is handled by the .statusBarHidden modifier
}
.overlay {
// Toolbar overlay
ClockToolbar(
isDisplayMode: viewModel.isDisplayMode,
onSettingsTap: { showSettings = true }
)
}
// MARK: - Private Methods
private func setTabBarHidden(_ hidden: Bool, animated: Bool) {
// This will be handled by the View extension
// For now, we'll keep the UIKit implementation
#if canImport(UIKit)
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first,
let tabBarController = window.rootViewController?.findTabBarController() else { return }
let tabBar = tabBarController.tabBar
let changes = {
tabBar.alpha = hidden ? 0 : 1
.overlay {
// Tab bar management overlay
ClockTabBarManager(isDisplayMode: viewModel.isDisplayMode, animated: true)
}
.overlay {
// Gesture handling overlay
ClockGestureHandler {
viewModel.toggleDisplayMode()
}
if animated {
UIView.animate(withDuration: AppConstants.AnimationDurations.short, animations: changes)
} else {
changes()
}
tabBar.isUserInteractionEnabled = !hidden
#endif
}
}

View File

@ -0,0 +1,52 @@
//
// ClockDisplayContainer.swift
// TheNoiseClock
//
// Created by Matt Bruce on 9/8/25.
//
import SwiftUI
/// Container component that handles the main clock display layout and scaling
struct ClockDisplayContainer: View {
// MARK: - Properties
let currentTime: Date
let style: ClockStyle
let isDisplayMode: Bool
// MARK: - Body
var body: some View {
GeometryReader { geometry in
ZStack {
// Time display - fills entire available space
TimeDisplayView(
date: currentTime,
use24Hour: style.use24Hour,
showSeconds: style.showSeconds,
digitColor: style.digitColor,
glowIntensity: style.glowIntensity,
manualScale: style.digitScale,
stretched: style.stretched,
showAmPmBadge: style.showAmPmBadge,
clockOpacity: style.clockOpacity,
fontFamily: style.fontFamily,
fontWeight: style.fontWeight,
fontDesign: style.fontDesign
)
.transition(.opacity)
}
.scaleEffect(isDisplayMode ? 1.0 : 0.995)
.animation(UIConstants.AnimationCurves.smooth, value: isDisplayMode)
}
}
}
// MARK: - Preview
#Preview {
ClockDisplayContainer(
currentTime: Date(),
style: ClockStyle(),
isDisplayMode: false
)
}

View File

@ -0,0 +1,32 @@
//
// ClockGestureHandler.swift
// TheNoiseClock
//
// Created by Matt Bruce on 9/8/25.
//
import SwiftUI
/// Component that handles gesture interactions for the clock view
struct ClockGestureHandler: View {
// MARK: - Properties
let onLongPress: () -> Void
// MARK: - Body
var body: some View {
EmptyView()
.contentShape(Rectangle())
.simultaneousGesture(
LongPressGesture(minimumDuration: AppConstants.DisplayMode.longPressDuration)
.onEnded { _ in
onLongPress()
}
)
}
}
// MARK: - Preview
#Preview {
ClockGestureHandler(onLongPress: {})
}

View File

@ -0,0 +1,38 @@
//
// ClockOverlayContainer.swift
// TheNoiseClock
//
// Created by Matt Bruce on 9/8/25.
//
import SwiftUI
/// Container component that handles the positioning and display of top overlays
struct ClockOverlayContainer: View {
// MARK: - Properties
let style: ClockStyle
// MARK: - Body
var body: some View {
VStack {
if style.showBattery || style.showDate {
TopOverlayView(
showBattery: style.showBattery,
showDate: style.showDate,
color: style.digitColor.opacity(UIConstants.Opacity.primary),
opacity: style.overlayOpacity
)
.padding(.top, UIConstants.Spacing.small)
.padding(.horizontal, UIConstants.Spacing.large)
.transition(.opacity)
}
Spacer()
}
}
}
// MARK: - Preview
#Preview {
ClockOverlayContainer(style: ClockStyle())
}

View File

@ -0,0 +1,55 @@
//
// ClockTabBarManager.swift
// TheNoiseClock
//
// Created by Matt Bruce on 9/8/25.
//
import SwiftUI
/// Component that manages tab bar visibility for display mode
struct ClockTabBarManager: View {
// MARK: - Properties
let isDisplayMode: Bool
let animated: Bool
// MARK: - Body
var body: some View {
EmptyView()
.onAppear {
setTabBarHidden(isDisplayMode, animated: false)
}
.onDisappear {
setTabBarHidden(false, animated: false)
}
.onChange(of: isDisplayMode) { _, newValue in
setTabBarHidden(newValue, animated: animated)
}
}
// MARK: - Private Methods
private func setTabBarHidden(_ hidden: Bool, animated: Bool) {
#if canImport(UIKit)
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first,
let tabBarController = window.rootViewController?.findTabBarController() else { return }
let tabBar = tabBarController.tabBar
let changes = {
tabBar.alpha = hidden ? 0 : 1
}
if animated {
UIView.animate(withDuration: AppConstants.AnimationDurations.short, animations: changes)
} else {
changes()
}
tabBar.isUserInteractionEnabled = !hidden
#endif
}
}
// MARK: - Preview
#Preview {
ClockTabBarManager(isDisplayMode: false, animated: false)
}

View File

@ -0,0 +1,47 @@
//
// ClockToolbar.swift
// TheNoiseClock
//
// Created by Matt Bruce on 9/8/25.
//
import SwiftUI
/// Component that handles the clock view toolbar and navigation
struct ClockToolbar: View {
// MARK: - Properties
let isDisplayMode: Bool
let onSettingsTap: () -> Void
// MARK: - Body
var body: some View {
EmptyView()
.navigationTitle(isDisplayMode ? "" : "Clock")
.toolbar {
if !isDisplayMode {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
onSettingsTap()
} label: {
Image(systemName: "gear")
.font(.title2)
.transition(.opacity)
}
}
}
}
.navigationBarBackButtonHidden(isDisplayMode)
.toolbar(isDisplayMode ? .hidden : .automatic)
}
}
// MARK: - Preview
#Preview {
NavigationStack {
ClockToolbar(
isDisplayMode: false,
onSettingsTap: {}
)
}
}