SwiftUI navigation back to first screen - swiftui

I am trying to understand how to organize my app.
Below you can find how I want my app to be organize
Question :
How to go back from DetailView to HomeView and "re-initialize" navigation ?
My Actual code to go back to First Screen
var body: some View {
VStack{
NavigationLink(destination: HomeView(), tag: 1, selection: $action){}
HStack{
Button(action: {
self.action = 1
},label: {
Text("Home")
})
}.padding()
HomeView :
var body: some View {
NavigationView{
VStack{
NavigationLink(destination: ParamsView()){Text("Settings")}
If I navigate to :
HomeView > ParamsView > GeneralView > Tab1 > Detail > Home Button
And do it multiple times here is the result :

Related

Closing A View From Another View

I am still learning SwiftUI and I have come across a small problem.
In my app, I have a main view. On the top is a search bar and at the bottom, a menu with different buttons. I want to change views when clicking those buttons. However, I only want to change the middle section.
No big deal, I will just put the middle part into a NavigationView. That works alright and I am able to change my views. My problem is that the buttons below do not have any impact on the new view.
To try to simplify: Let’s say I’m on home page. I then click the grocery list button (guess what I’m making school projects lol). My navigation link works just fine and goes to the list. So, now I’m on view 2 let’s say. When I press the home button, it doesn’t close that view and go to my main one. Here is my code setup:
import SwiftUI
struct ContentView: View {
#State private var searchText: String = ""
#State private var action: Int? = 0
var body: some View {
ZStack {
// Top Menu
VStack{
HStack {
Spacer()
TextField("Search",
text: $searchText)
.background(Color.white)
Button(action: {
self.action = 1
}, label: {
Image(systemName: "magnifyingglass.circle")
.font(.largeTitle)
})
Spacer()
}
// Body
NavigationView {
VStack {
Text("Can I See Something")
NavigationLink(destination: SearchView(), tag: 1, selection: $action) {
}
Text("Yes/No")
}
}
Spacer()
// Bottom Menu
HStack (alignment: .top) {
Spacer()
VStack {
Button(action: {
}, label: {
Image(systemName: "house.fill")
.font(.largeTitle)
})
.padding(.top)
Text("Home")
}
Divider()
.padding(.horizontal)
.frame(width: 2.5, height: 100)
VStack {
Button(action: {
}, label: {
Image(systemName: "newspaper")
.font(.largeTitle)
})
.padding(.top)
Text("Weekly\nAd")
.multilineTextAlignment(.center)
}
Divider()
.padding(.horizontal)
.frame(width: 2.5, height: 100)
VStack {
Button(action: {
}, label: {
Image(systemName: "checklist")
.font(.largeTitle)
})
.padding(.top)
Text("Grocery\nList")
.multilineTextAlignment(.center)
}
Divider()
.padding(.horizontal)
.frame(width: 2.5, height: 100)
VStack {
Button(action: {
}, label: {
Image(systemName: "person.crop.circle")
.font(.largeTitle)
})
.padding(.top)
Text("Account")
}
Spacer()
}
}
}
}
}
struct SearchView: View {
var body: some View {
ZStack {
Text("Nothing to see here!")
}
.navigationBarBackButtonHidden(true)
}
}
SearchView is a separate view (in its own file) in the app that opens up when the magnifying glass button is pressed. Currently it does not do anything. However I want to be able to press those buttons on this view above to still navigate the app.
Also, on another note, is there anyway to get rid of the back button?
In your code the buttons do not have any function.
Instead of creating a tab bar on your own, I'd rather take something like:
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
MainView()
.tabItem {
Label("Home", systemImage: "house.fill")
}
NewsView()
.tabItem {
Label("Weekly\nAd", systemImage: "newspaper")
}
OrderView()
.tabItem {
Label("Grocery\nList", systemImage: "checklist")
}
AccountView()
.tabItem {
Label("Account", systemImage: "person.crop.circle")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct MainView: View {
var body: some View {
Text("Home View")
}
}
struct NewsView: View {
var body: some View {
Text("News View")
}
}
struct OrderView: View {
var body: some View {
Text("Order View")
}
}
struct AccountView: View {
var body: some View {
Text("Account View")
}
}
In that case you'll have to create a view for each tab you are configuring (see the last 4 structs).
If you want to do it with a Stack with your own created buttons, I think you should create al 4 views as well and then you either hide them or put them out of focus by using an offset. In that case the buttons should hide/show the specific views or change the offset accordingly to move the specific views into the visible area. With the offset you also can add some animation.
Regarding the search bar on top of your app, since the views are all different, I wouldn't keep the same search bar everywhere, but if you really want to have it that way, you can embed the code + your search bar into a VStack (as you did it in your example).

Navigation Link Returning Back Two Levels on Exit

I am having trouble with a return from a navigation view within a tabbed view. My project has a Settings tab where the user may select via navigation link "View Entries". And from there another navigation link to "Add New Entry". Returning from Add New Entry should bring you to View Entries but instead is return another level to the Setting Menu.
I am seeing a warning on the console stating "trying to pop to a missing destination at /Library/Caches/com.apple...". Using the tabbed view sample code at SwiftUI NavigationView trying to pop to missing destination (Monoceros?) I no longer get the "pop-to-missing-destination" warning but I still have the same problem with the navigation return.
The sample code below is ready to run and test in Xcode 12.
In the sample code below, tap settings and select the navigation view "View Entries". This would be a screen where entries are displayed in a list. Tapping the plus button is where new entries could be added. The textfield on the "Add New Entry" screen doesn't do anything. Clicking the Save or Back buttons should return you to "View Entries" screen but instead returns you to the Setting Menu. The Save button uses presentationMode.wrappedValue.dismiss to dismiss the view.
The fact that two different version of the tab view logic didn't have any impact on my navigation view return logic leads me to believe that I just have some kind on plain old bug in my navigation view logic but I sure don't see one. The sample code below is using the standard tab view logic.
struct ContentView: View {
#State private var selection = 0
var body: some View {
NavigationView {
TabView (selection: $selection) {
HomeView()
.tabItem {
Label("Home", systemImage: "house")
}.tag(1)
AView()
.tabItem {
Label("A", systemImage: "a.circle")
}.tag(2)
BView()
.tabItem {
Label("B", systemImage: "b.circle")
}.tag(3)
SettingsView()
.tabItem {
Label("Settings", systemImage: "gearshape")
}.tag(4)
}
}
}
}
struct HomeView: View {
var body: some View {
Text("Home Screen")
}
}
struct AView: View {
var body: some View {
Text("A Screen")
}
}
struct BView: View {
var body: some View {
Text("B Screen")
}
}
struct SettingsView: View {
var body: some View {
VStack (alignment: .leading) {
List {
Text("Settings")
.font(.title)
.fontWeight(.bold)
.padding(.leading, 15)
NavigationLink(destination: SetAView()) {Text("View Entries")}
}
}
.font(.body)
}
}
struct SetAView: View {
var body: some View {
List {
Text("View Entries")
.padding(.vertical, 10)
Text("Normally entires would be displayed here")
Text("Should return here upon adding new entry")
.padding(.vertical, 10)
Text("Click the + button to add new entry")
}
.navigationBarItems(trailing: NavigationLink (destination: AddTestView()) {
Image(systemName: "plus")
.resizable()
.foregroundColor(Color(.systemBlue))
.frame(width: 18, height: 18)
} // body
)
}
}
struct AddTestView: View {
#Environment(\.presentationMode) var presentationMode
#State private var catSelect: String = ""
var body: some View {
NavigationView {
VStack {
Form {
Section {
TextField("Enter Entry Name", text: $catSelect)
.padding(.horizontal, 20)
.keyboardType(.default)
}
}
}
.navigationBarTitle(Text("Add new Entry"), displayMode: .inline)
.navigationViewStyle(StackNavigationViewStyle())
.navigationBarItems(trailing: Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Text ("Save")
})
}
}
}
After considerable analysis I discovered in my actual code that I had two copies of NavigationView––nothing wrong with the TabView code. Removing the one NavigationView not in contentView then caused several functions from working so rebuilt them from scratch. Now have everything working with TabView and NaigationView including the back buttons.

SwiftUI - TabView with nested NavigationViews resets Navigationflow

I'm having a TabView with Navigation Views in it.
struct ContentView: View {
var body: some View {
TabView {
NavigationView {
NavigationLink("Link to my first Navigation Level", destination: MyFirstView())
}
.tabItem {
Image(systemName: "house")
Text("Home")
}.tag(0)
NavigationView {
Text("Second Nav Page")
}
.tabItem {
Image(systemName: "gear")
Text("Settings")
}.tag(1)
}
}
}
struct MyFirstView: View {
#State var selectedTag: String?
var body: some View {
VStack {
Text("My First View")
NavigationLink("(Working) Link to my second Navigation Level", destination: MySecondView())
Text("But the Button in the Navigation Bar doesn't work")
}
.navigationBarTitle("My First View", displayMode: .inline)
.navigationBarItems(
leading: HStack {
NavigationLink(destination: MySecondView(), tag: "xx", selection: $selectedTag ){
Button(action: {
print("Settings button pressed...")
self.selectedTag = "xx"
}) {
Image(systemName: "gearshape.2.fill").imageScale(.large)
}
}
}
)
}
}
struct MySecondView: View {
var body: some View {
Text("My Second View")
}
}
Now I got a super weired behavior. If I click on "Link to my first Navigation Level" and then on "(Working) Link to my second Naviation Level" the journey works. If I click on "Back" while being in the second Navigation Level it goes back to the first Navigation Level.
Issue:
When I click on the gear symbol in the Navbar on the first Navigation Level it kind of escapes from the nested Navigation and sets it to the top level. This has the consequence that when I click on "Back" it brings me from the Second Navigation Level back to the very root screen but my expected behavior is that it should go back to the first Navigation Level.
Any ideas what I'm doing wrong? I'm using Xcode 12.2 beta 3 and iOS 14.2 (not sure if it's a beta bug).
Many Thanks!
This did the trick (thanks #Asperi). Has to be outside tabBarItems
NavigationLink(destination: AddDetailView(existingItem: nil),
isActive: $addMode) { EmptyView() }

Two NavigationBar Items appears twice in the second current view

I have a current SwiftUI View with a Navigation Bar item . I created a Back Button on this current view so that the title is not inherited from the previous view (NEWSview) with list of news websites to navigate to the current view and clicking on the back button returns me to the previous view. This is sample code of the previous view screen how i did:
struct ContentView: View {
#ObservedObject var networkManager = NetworkManager()
var body: some View {
NavigationView {
List (networkManager.posts) { post in
NavigationLink (destination: DetailView(url: post.url) ) {
HStack {
Text(String(post.points))
Text(post.title)
}
}
}
.navigationBarTitle("News")
}
.onAppear {
DispatchQueue.main.async {
self.networkManager.fetchData()
}
}
}
}
This is the current screen view that is having the two buttons:
struct DetailView: View {
let url : String?
#Environment(\.presentationMode) private var mode
var body: some View {
WebView(urlString: url)
.navigationBarItems(leading:
Button(action: {
self.mode.wrappedValue.dismiss()
}, label: {
HStack(spacing: 10) {
Image(systemName: "chevron.left")
.resizable()
.frame(width: 10.0, height: 19.0)
.font(.headline)
Text("Back")
}
})
)
}
}
Now the issue is : The current screen NavBar has two buttons like this : one with news title and the back button (Picture attached) . I want only to remain with the Back Button on the current view that on click will navigate back to the previous root view . I thought what i did is right but missing something . If remove the entire code with the button in the current view , i will only see the "NEWS" navigation that on click will return me to the News View but i want a back title that should not be inherited from the news view .

Hidden Navbar still pushes down view

I have a:
contentView()
SignUpView()
SignInView()
The contentView calls the SignInView()
struct ContentView: View {
var body: some View {
NavigationView {
SignInView()
}
}
}
In my SignUpView() I have:
var body: some View {
VStack(alignment: .leading) {
NavigationLink(destination: SignInView()) {
Text("Sign in")
.fontWeight(.semibold)
.foregroundColor(Color("startColor"))
}
}.navigationBarHidden(true)
In my SigbInView I have:
var body: some View {
VStack(alignment: .leading) {
NavigationLink(destination: SignUpView()) {
Text("Sign up")
.fontWeight(.semibold)
.foregroundColor(Color("startColor"))
}.navigationBarHidden(true)
Im using .navigationBarHidden(true) to hide the bar, but the < back still appears in the top left hand corner to take you back to the previous screen, Iv also tried adding the navbar text = "" and setting the property to .inline
Im trying to only use these navigationLinks on the SignInView and SignUpViews to navigate, i don't want the bar to appear or push the view down.
So it looks like another property can be set to true to hide the back button:
.navigationBarBackButtonHidden(true)
This worked for me.