From 6734318e77e4ff057802d3d9f1ee94aef6e00028 Mon Sep 17 00:00:00 2001 From: elsong86 Date: Sat, 18 Jan 2025 13:56:36 -0800 Subject: [PATCH] Modularized root view in iOS application. --- .../View/ActionButtonsView.swift | 52 ++++++ ios/taco-about-it-ios/View/ContentView.swift | 169 ++++++------------ ios/taco-about-it-ios/View/Header.swift | 45 ++--- .../View/PlaceholderView.swift | 25 ++- .../View/TitleSectionView.swift | 52 ++++++ .../ViewModel/ContentViewModel.swift | 12 +- 6 files changed, 205 insertions(+), 150 deletions(-) create mode 100644 ios/taco-about-it-ios/View/ActionButtonsView.swift create mode 100644 ios/taco-about-it-ios/View/TitleSectionView.swift diff --git a/ios/taco-about-it-ios/View/ActionButtonsView.swift b/ios/taco-about-it-ios/View/ActionButtonsView.swift new file mode 100644 index 0000000..fed093c --- /dev/null +++ b/ios/taco-about-it-ios/View/ActionButtonsView.swift @@ -0,0 +1,52 @@ +import SwiftUI + +struct ActionButtonsView: View { + @ObservedObject var viewModel: ContentViewModel + @Binding var searchText: String + @Binding var destination: ContentView.Destination? + + var body: some View { + VStack { + // Share Location Button + Button(action: { + viewModel.requestLocation() + print("Requesting location...") + }) { + Label("Share Location", systemImage: "location.fill") + .frame(maxWidth: .infinity) + } + .padding() + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(10) + .shadow(color: .black.opacity(0.5), radius: 5, x: 2, y: 2) + + Text("OR") + .foregroundColor(.black) + .padding(.vertical, 8) + + // Search Bar + SearchBarView( + searchText: $searchText, + onSearch: { text in + print("Setting navigationTarget to .search with \(searchText)") + destination = .search(text) + } + ) + .frame(maxWidth: .infinity) // Make search bar fill available width + .padding(.horizontal, 16) + .padding(.vertical, 8) + .background(Color.white) + .cornerRadius(8) + .shadow(color: .gray.opacity(0.5), radius: 5, x: 2, y: 2) + } + } +} + +#Preview { + ActionButtonsView( + viewModel: ContentViewModel(), + searchText: .constant(""), + destination: .constant(nil) + ) +} diff --git a/ios/taco-about-it-ios/View/ContentView.swift b/ios/taco-about-it-ios/View/ContentView.swift index f3f1eeb..868fc57 100644 --- a/ios/taco-about-it-ios/View/ContentView.swift +++ b/ios/taco-about-it-ios/View/ContentView.swift @@ -1,20 +1,20 @@ import SwiftUI struct ContentView: View { - @State private var searchText: String = "" // State for search bar input - @State private var readyToNavigateWithSearch: Bool = false // State to trigger navigation for search bar - @State private var selectedSearchString: String? // Holds the search string for navigation - - @StateObject private var viewModel = ContentViewModel() // ViewModel for location handling - @State private var selectedLocation: GeoLocation? - @State private var readyToNavigateWithLocation: Bool = false // State to trigger navigation for location + @State private var searchText: String = "" + @StateObject private var viewModel = ContentViewModel() + @State private var destination: Destination? + + enum Destination: Hashable { + case location(GeoLocation) + case search(String) + } let tacoSpotCharacters: [(character: String, color: Color)] = [ ("T", Color(hex: "#9F1239")), // Rose 800 ("A", Color(hex: "#065F46")), // Emerald 800 ("C", Color(hex: "#D97706")), // Yellow 600 ("O", Color(hex: "#C2410C")), // Orange 700 - (" ", .clear), // Space between words ("S", Color(hex: "#9F1239")), // Rose 800 ("P", Color(hex: "#065F46")), // Emerald 800 ("O", Color(hex: "#D97706")), // Yellow 600 @@ -23,128 +23,61 @@ struct ContentView: View { var body: some View { NavigationStack { - ZStack(alignment: .top) { - // Background Image - Image("hero") - .resizable() - .scaledToFill() - .frame(maxWidth: .infinity, maxHeight: .infinity) - .edgesIgnoringSafeArea(.all) - .clipped() - - LinearGradient( - gradient: Gradient(colors: [ - Color.white.opacity(0.75), - Color.clear - ]), - startPoint: .leading, - endPoint: .trailing - ) - .edgesIgnoringSafeArea(.all) - - Rectangle() - .fill(Color.white.opacity(0.65)) - .edgesIgnoringSafeArea(.all) - - VStack(spacing: 0) { - // Header - Header() - .frame(maxWidth: .infinity) - .background(Color.white.opacity(0.9)) - .shadow(color: .gray.opacity(0.5), radius: 5, x: 0, y: 2) - .edgesIgnoringSafeArea(.top) - } - - VStack(spacing: 16) { - Spacer() - .frame(height: 80) - - Text("Anywhere, Anytime") - .font(Font.custom("ThirstyRoughReg", size: 20)) - .foregroundColor(.black) - - Text("Find Your New") - .font(Font.custom("BrothersRegular", size: 30)) - .foregroundColor(.black) - - Text("Favorite") - .font(Font.custom("BrothersRegular", size: 30)) - .foregroundColor(.black) - - HStack(spacing: 0) { - ForEach(tacoSpotCharacters, id: \.character) { item in - Text(item.character) - .foregroundColor(item.color) - .font(Font.custom("BrothersRegular", size: 50)) - } - } - - Text("Share or enter your location to get started") - .foregroundColor(.black) - - Divider() - - // Share Location Button - Button(action: { - viewModel.requestLocation() - print("Requesting location...") - }) { - Label("Share Location", systemImage: "location.fill") + VStack(spacing: 0) { + // Header + Header() + .padding(.bottom, 16) // Spacing below the header + + // Main Content + ScrollView { + VStack(spacing: 16) { + Spacer() + .frame(height: 40) // Reduced height from 80 to 40 + + // Title Section + TitleSectionView(tacoSpotCharacters: tacoSpotCharacters) + + // Instruction Text + Text("Share or enter your location to get started") + .foregroundColor(.black) + .multilineTextAlignment(.center) + .padding(.horizontal, 16) + + Divider() + + // Action Buttons + ActionButtonsView( + viewModel: viewModel, + searchText: $searchText, + destination: $destination + ) } .padding() - .background(Color.blue) - .foregroundColor(.white) - .cornerRadius(10) - .shadow(color: .black.opacity(0.5), radius: 5, x: 2, y: 2) - - Text("OR") - .foregroundColor(.black) - - // Search Bar - SearchBarView( - searchText: $searchText, - onSearch: {_ in - selectedSearchString = searchText // Set the search string - readyToNavigateWithSearch = true // Trigger navigation - } - ) - .frame(maxWidth: UIScreen.main.bounds.width) - .padding(.horizontal, 16) - .padding(.vertical, 8) - .cornerRadius(8) - .shadow(color: .gray.opacity(0.5), radius: 5, x: 2, y: 2) } - .padding() } - // Navigation for Share Location - .onChange(of: viewModel.location) { oldValue, newValue in + .background(Color.white) // Optional: Set a background color if desired + .onChange(of: viewModel.location) { _, newValue in if let location = newValue { - selectedLocation = location - readyToNavigateWithLocation = true + destination = .location(location) } } - .navigationDestination(isPresented: $readyToNavigateWithLocation) { - if let location = selectedLocation { - PlaceholderView(location: location) - .onDisappear { - readyToNavigateWithLocation = false - selectedLocation = nil - } - } - } - // Navigation for Search Bar - .navigationDestination(isPresented: $readyToNavigateWithSearch) { - if let searchString = selectedSearchString { - PlaceholderView(searchString: searchString) - .onDisappear { - readyToNavigateWithSearch = false - selectedSearchString = nil - } + .navigationDestination(item: $destination) { destination in + switch destination { + case .location(let loc): + PlaceholderView(location: loc, searchString: nil, onDisappear: { + viewModel.resetLocation() + }) + case .search(let query): + PlaceholderView(location: nil, searchString: query, onDisappear: { + // If you have a separate state for search, reset it here + // For example: viewModel.resetSearch() + }) } } } } } + // MARK: - PreviewProvider struct ContentView_Previews: PreviewProvider { static var previews: some View { diff --git a/ios/taco-about-it-ios/View/Header.swift b/ios/taco-about-it-ios/View/Header.swift index f2c4158..d6baaa1 100644 --- a/ios/taco-about-it-ios/View/Header.swift +++ b/ios/taco-about-it-ios/View/Header.swift @@ -16,32 +16,35 @@ struct Header: View { ] var body: some View { - VStack { // Wrap the HStack in a VStack to add padding above - Spacer() - .frame(height: 20) // Add space above the content (adjust as needed) - - HStack { - // Logo - Image("logo") + HStack { + // Logo + Image("logo") + .resizable() + .scaledToFit() + .frame(height: 40) // Adjust size as needed - // Title - HStack(spacing: 0) { - ForEach(title, id: \.character) { item in - Text(item.character) - .foregroundColor(item.color) - .font(Font.custom("HustlersRoughDemo", size: 30)) - } + // Title + HStack(spacing: 0) { + ForEach(title, id: \.character) { item in + Text(item.character) + .foregroundColor(item.color) + .font(Font.custom("HustlersRoughDemo", size: 30)) } + } - Spacer() // Push the menu icon to the far right + Spacer() // Push the menu icon to the far right - // Menu Icon - Image(systemName: "line.3.horizontal") - } - .frame(maxWidth: UIScreen.main.bounds.width * 0.9) // Constrain to screen width + // Menu Icon + Image(systemName: "line.3.horizontal") + .resizable() + .scaledToFit() + .frame(width: 24, height: 24) // Adjust icon size } - .frame(maxWidth: .infinity) // Ensure the header stretches to the full width - .padding(.top, 20) // Add padding at the top of the header (for additional space) + .padding(.horizontal) + .padding(.vertical, 8) + .background(Color.white.opacity(0.9)) // Apply background with opacity + .shadow(color: .gray.opacity(0.5), radius: 5, x: 0, y: 2) // Shadow + .frame(maxWidth: .infinity) // Stretch to the full width of the screen } } diff --git a/ios/taco-about-it-ios/View/PlaceholderView.swift b/ios/taco-about-it-ios/View/PlaceholderView.swift index 954d16e..ac4c447 100644 --- a/ios/taco-about-it-ios/View/PlaceholderView.swift +++ b/ios/taco-about-it-ios/View/PlaceholderView.swift @@ -1,21 +1,32 @@ import SwiftUI struct PlaceholderView: View { - var location: GeoLocation? = nil - var searchString: String? = nil - + var location: GeoLocation? + var searchString: String? + var onDisappear: () -> Void // Closure to reset location + var body: some View { VStack { if let location = location { - Text("Latitude: \(location.latitude)") - Text("Longitude: \(location.longitude)") + Text("Location: \(location.latitude), \(location.longitude)") + .font(.title) + .padding() } else if let searchString = searchString { Text("Search Query: \(searchString)") + .font(.title) + .padding() } else { Text("No data available.") + .font(.title) + .padding() } } - .padding() - .navigationTitle("Results") + .onDisappear { + onDisappear() // Call the closure when the view disappears + } } } + +#Preview { + PlaceholderView(location: GeoLocation(latitude: 37.7749, longitude: -122.4194), searchString: nil, onDisappear: {}) +} diff --git a/ios/taco-about-it-ios/View/TitleSectionView.swift b/ios/taco-about-it-ios/View/TitleSectionView.swift new file mode 100644 index 0000000..d079126 --- /dev/null +++ b/ios/taco-about-it-ios/View/TitleSectionView.swift @@ -0,0 +1,52 @@ +import SwiftUI + +struct TitleSectionView: View { + // Array of characters with their respective colors + let tacoSpotCharacters: [(character: String, color: Color)] + + var body: some View { + VStack(spacing: 16) { + // First Line of Title + Text("Anywhere, Anytime") + .font(Font.custom("ThirstyRoughReg", size: 20)) + .foregroundColor(.black) + .multilineTextAlignment(.center) + + // Second Line of Title + Text("Find Your New") + .font(Font.custom("BrothersRegular", size: 30)) + .foregroundColor(.black) + .multilineTextAlignment(.center) + + // Third Line of Title + Text("Favorite") + .font(Font.custom("BrothersRegular", size: 30)) + .foregroundColor(.black) + .multilineTextAlignment(.center) + + // Colored Characters Line + HStack(spacing: 0) { + ForEach(tacoSpotCharacters, id: \.character) { item in + Text(item.character) + .foregroundColor(item.color) + .font(Font.custom("BrothersRegular", size: 50)) + } + } + } + .frame(maxWidth: .infinity) // Ensures the VStack takes the full available width + } +} + +#Preview { + // Sample Preview with mock characters and colors + TitleSectionView(tacoSpotCharacters: [ + ("T", Color(hex: "#9F1239")), // Rose 800 + ("A", Color(hex: "#065F46")), // Emerald 800 + ("C", Color(hex: "#D97706")), // Yellow 600 + ("O", Color(hex: "#C2410C")), // Orange 700 + ("S", Color(hex: "#9F1239")), // Rose 800 + ("P", Color(hex: "#065F46")), // Emerald 800 + ("O", Color(hex: "#D97706")), // Yellow 600 + ("T", Color(hex: "#9F1239")) // Rose 800 + ]) +} diff --git a/ios/taco-about-it-ios/ViewModel/ContentViewModel.swift b/ios/taco-about-it-ios/ViewModel/ContentViewModel.swift index 4318ffd..82b0787 100644 --- a/ios/taco-about-it-ios/ViewModel/ContentViewModel.swift +++ b/ios/taco-about-it-ios/ViewModel/ContentViewModel.swift @@ -5,20 +5,24 @@ import CoreLocation class ContentViewModel: ObservableObject { @Published var location: GeoLocation? // Updated to GeoLocation @Published var errorMessage: String? - + private let locationManager = LocationManager() private var cancellables = Set() - + init() { locationManager.$location .map { $0.map { GeoLocation(latitude: $0.latitude, longitude: $0.longitude) } } // Convert CLLocationCoordinate2D to GeoLocation .assign(to: &$location) - + locationManager.$errorMessage .assign(to: &$errorMessage) } - + func requestLocation() { locationManager.requestLocation() } + + func resetLocation() { + self.location = nil + } }