I'm having trouble with navigation bar in SwiftUI. I have 3 views:
- ViewOne: contains the navigation view and two tab views
- ViewTwo: contains multiple navigation links
- ViewThree: contains multiple navigation links
I know you should only have one navigation view in the navigation which I have in the content view.
However, if I go from ViewOne > ViewTwo > ViewThree and then back to ViewTwo I get double navigation bar like this
And here's my code:
ViewOne
import SwiftUI
struct ContentView: View {
@State private var activateNavigationLink: Bool = false // Required to navigate back to this view after deleting a trip
var body: some View {
TabView {
NavigationView {
ViewTwo()
}.tabItem{
Image(systemName: "airplane")
Text("Trips")
}
NavigationView {
ViewThree(activateNavigationLink: $activateNavigationLink)
}
.tabItem{
Image(systemName: "location.fill")
Text("Nearby")
}
}
}
}
ViewTwo
import SwiftUI
struct ViewTwo: View {
@State private var cityName: String = ""
@State private var action: Int? = 0
@Binding var activateNavigationLink: Bool // Required to navigate back to itinerary view after deleting a trip
@StateObject var selectCityVM = SelectCityViewModel()
@Environment(\.managedObjectContext) private var viewContext
@State private var showingAlert = false
@StateObject var locationVM = UserLocationViewModel()
@State var trip: Trip? = nil
@State var location: Destination? = nil {
didSet {
if let name = location?.city {
cityName = name // Set city name for navbar
print(cityName)
}
}
}
let categories = ["Food", "Things to do", "Nightlife", "Need to know"] // Category tile names
let columns = [GridItem(.adaptive(minimum: 150))] // Adjust column layout here
var body: some View {
VStack {
VStack { // Needed to navigate to trip edit screen
NavigationLink(destination: CreateTripView(activateNavigationLink: $activateNavigationLink, trip: trip), tag: 1, selection: $action) {
EmptyView()
}
}
// check if saved trip or nearby query
if trip != nil {
HStack {
// Displays trip start and end dates
if let tripStartDate = trip?.startDate {
if let tripEndDate = trip?.endDate {
// Displays trip start and end dates
Text("\(currentYearDate.string(from: tripStartDate)) - \(futureYearDate.string(from: tripEndDate))")
.padding(.bottom, 3)
}
}
Spacer()
}.padding(.leading)
}
Spacer()
ScrollView {
// Displays category tiles
LazyVGrid(columns: columns, spacing: 30) {
ForEach(categories, id: \.self) { category in
switch category {
case "Need to know": // Show country info
NavigationLink(destination: DestinationInfoView(trip: trip)) {
CategoryCard(category: category)
}
default: // Search yelp
NavigationLink(destination: ViewThree(selectedCategory: category, trip: trip, location: location)) {
CategoryCard(category: category)
}
}
}
}
}.padding(.top)
Spacer()
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
if trip != nil {
Menu {
// Edit trip
Button("Edit trip") {
action = 1 // Navigate to trip edit screen
}
// Delete trip
Button("Delete trip") {
showAlert() // Show alert to confirm deletion of trip
}
} label: {
Image(systemName: "ellipsis.circle")
.font(.title2)
}
}
}
}
.navigationTitle("\(cityName ) \(selectCityVM.getFlag(from: trip?.iso2 ?? ""))")
}
// Show alert to confirm deletion of trip
.alert(isPresented:$showingAlert) {
Alert(
title: Text("Are you sure you want to delete this?"),
message: Text("There is no undo"),
primaryButton: .destructive(Text("Delete")) {
deleteTrip()
},
secondaryButton: .cancel()
)
}
}
ViewThree
import SwiftUI
struct ViewThree: View {
@ObservedObject var yelpVM = YelpSearchViewModel()
@State private var showingLocationAlert = false
@State var selectedCategory: String = ""
var trip: Trip?
var location: Destination?
var body: some View {
VStack {
if yelpVM.places.isEmpty { // Show loading view
Spacer()
VStack {
ProgressView()
.scaleEffect(1.5, anchor: .center)
.padding()
Text("Finding local \(selectedCategory.lowercased())...").padding(5)
}
Spacer()
} else {
ScrollView {
LazyVStack {
if let fetchedPlacess = yelpVM.places {
ForEach(fetchedPlacess, id: \.identifier) { place in
NavigationLink(destination: BusinessDetailsView(business: place)) {
ListingCardView(business: place)
}.buttonStyle(PlainButtonStyle())
.onAppear(perform: {
// Get more restaurants from yelp if the user has reached the end of current page
if place == yelpVM.places.last {
yelpVM.getLocalPlaces(category: selectedCategory, latitude: place.coordinates?.latitude ?? 0, longitude: place.coordinates?.longitude ?? 0)
}
})
}
}
}
}
}
}.navigationTitle(selectedCategory)
.onAppear(perform: {
if trip != nil {
if let unwrappedTrip = trip {
if yelpVM.places.isEmpty {
yelpVM.getLocalPlaces(category: selectedCategory, latitude: unwrappedTrip.latitude, longitude: unwrappedTrip.longitude)
}
}
} else {
if let unwrappedlocation = location {
if yelpVM.places.isEmpty {
yelpVM.getLocalPlaces(category: selectedCategory, latitude: unwrappedlocation.latitude, longitude: unwrappedlocation.longitude)
}
}
}
})
}
}
This has been driving me nuts. Any help or guidance would be greatly appreciated.