diff --git a/TheNoiseClock/ViewModels/ClockViewModel.swift b/TheNoiseClock/ViewModels/ClockViewModel.swift index 748aa4f..5155fe1 100644 --- a/TheNoiseClock/ViewModels/ClockViewModel.swift +++ b/TheNoiseClock/ViewModels/ClockViewModel.swift @@ -86,7 +86,7 @@ class ClockViewModel { } } - private func saveStyle() { + func saveStyle() { persistenceWorkItem?.cancel() let work = DispatchWorkItem { diff --git a/TheNoiseClock/Views/Clock/ClockView.swift b/TheNoiseClock/Views/Clock/ClockView.swift index 22f1c5b..8d5565c 100644 --- a/TheNoiseClock/Views/Clock/ClockView.swift +++ b/TheNoiseClock/Views/Clock/ClockView.swift @@ -13,6 +13,7 @@ struct ClockView: View { // MARK: - Properties @State private var viewModel = ClockViewModel() @State private var showSettings = false + @State private var showFullScreenHint = false // MARK: - Body var body: some View { @@ -31,6 +32,11 @@ struct ClockView: View { // Top overlay container ClockOverlayContainer(style: viewModel.style) + + // Full screen hint overlay + if showFullScreenHint { + FullScreenHintView(isDisplayMode: viewModel.isDisplayMode) + } } } } @@ -48,7 +54,19 @@ struct ClockView: View { // Toolbar overlay ClockToolbar( isDisplayMode: viewModel.isDisplayMode, - onSettingsTap: { showSettings = true } + onSettingsTap: { showSettings = true }, + onFullScreenTap: { + if !viewModel.isDisplayMode { + viewModel.toggleDisplayMode() + + // Show hint after a brief delay to allow transition + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + showFullScreenHint = true + } + } else { + viewModel.toggleDisplayMode() + } + } ) } .overlay { diff --git a/TheNoiseClock/Views/Clock/Components/ClockToolbar.swift b/TheNoiseClock/Views/Clock/Components/ClockToolbar.swift index fa14680..cc8f024 100644 --- a/TheNoiseClock/Views/Clock/Components/ClockToolbar.swift +++ b/TheNoiseClock/Views/Clock/Components/ClockToolbar.swift @@ -13,6 +13,7 @@ struct ClockToolbar: View { // MARK: - Properties let isDisplayMode: Bool let onSettingsTap: () -> Void + let onFullScreenTap: () -> Void // MARK: - Body var body: some View { @@ -20,7 +21,16 @@ struct ClockToolbar: View { .navigationTitle(isDisplayMode ? "" : "Clock") .toolbar { if !isDisplayMode { - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItemGroup(placement: .navigationBarTrailing) { + Button { + onFullScreenTap() + } label: { + Image(systemName: "rectangle.expand.diagonal") + .font(.title2) + .transition(.opacity) + } + .accessibilityLabel("Enter Full Screen Mode") + Button { onSettingsTap() } label: { @@ -28,6 +38,7 @@ struct ClockToolbar: View { .font(.title2) .transition(.opacity) } + .accessibilityLabel("Clock Settings") } } } @@ -41,7 +52,8 @@ struct ClockToolbar: View { NavigationStack { ClockToolbar( isDisplayMode: false, - onSettingsTap: {} + onSettingsTap: {}, + onFullScreenTap: {} ) } } diff --git a/TheNoiseClock/Views/Clock/Components/FullScreenHintView.swift b/TheNoiseClock/Views/Clock/Components/FullScreenHintView.swift new file mode 100644 index 0000000..372e41a --- /dev/null +++ b/TheNoiseClock/Views/Clock/Components/FullScreenHintView.swift @@ -0,0 +1,73 @@ +// +// FullScreenHintView.swift +// TheNoiseClock +// +// Created by Matt Bruce on 9/10/25. +// + +import SwiftUI + +/// Component that shows a subtle hint for how to exit full-screen mode +struct FullScreenHintView: View { + + // MARK: - Properties + let isDisplayMode: Bool + @State private var hintOpacity: Double = 0.0 + + // MARK: - Body + var body: some View { + if isDisplayMode { + VStack { + Spacer() + + HStack { + Image(systemName: "hand.point.up.left") + .font(.title2) + .foregroundColor(.white.opacity(0.7)) + + Text("Long press to exit full screen") + .font(.headline) + .foregroundColor(.white.opacity(0.7)) + + Image(systemName: "hand.point.up.left") + .font(.title2) + .foregroundColor(.white.opacity(0.7)) + } + .padding(.horizontal, 20) + .padding(.vertical, 12) + .background( + RoundedRectangle(cornerRadius: 20) + .fill(.black.opacity(0.6)) + .blur(radius: 1) + ) + .opacity(hintOpacity) + + Spacer() + .frame(height: 100) // Space above tab bar area + } + .transition(.opacity.combined(with: .scale(scale: 0.9))) + .onAppear { + withAnimation(.easeInOut(duration: 0.5)) { + hintOpacity = 1.0 + } + + // Auto-hide after 3 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { + withAnimation(.easeInOut(duration: 0.5)) { + hintOpacity = 0.0 + } + } + } + } + } + +} + +// MARK: - Preview +#Preview { + ZStack { + Color.black.ignoresSafeArea() + + FullScreenHintView(isDisplayMode: true) + } +}