SwiftUI: Hide Navigation Bar on Specific Screens - ios

It seems as though I'm unable to choose which pages I want to hide the Navigation Bar on in swift UI.
I have the following screens:
Main
struct Main: View {
var body: some View {
NavigationView {
Home()
}
}
}
Home
struct Home: View {
var body: some View {
NavigationLink(destination: Details()) {
Text("Go Next")
}
// I expect the navigation bar to show up here, and it does
.navigationBarTitle("Home")
.navigationBarHidden(false)
}
}
Details
struct Details: View {
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
var body: some View {
Button(action: { self.mode.wrappedValue.dismiss() }) {
Text("Go Back")
}
// I expect the navigation bar to be hidden here
// At first, it is hidden. Then, after a second, it pops up
// with the title "Details"
.navigationBarTitle("Details")
.navigationBarHidden(true)
}
}
Either I'm doing this wrong (likely), or Apple has some work to do (also likely).

As I saw earlier, something comes wrong, when you use both:
.navigationBarTitle("some text")
.navigationBarHidden(true)
in code below there is no navigation bar staff and nothing pops up:
struct Details: View {
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
var body: some View {
Button(action: { self.mode.wrappedValue.dismiss() }) {
Text("Go Back")
}
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true) // even no back button
}
}

Related

Multiple back buttons created when navigates to root screen in SwiftUI

say I create 3 screens in SwiftUI which contains a NavigationLink to the next screen. like, first screen navigates to 2nd screen. 2nd screen navigates to third. and the third screen navigates to the first screen. In this case even if I use NavigationView only once(in the first screen). I encountered that there's a back button forming when I navigate to 1st screen from the third screen. And it keeps adding up when I start to navigate from then on. I have tried to use .navigationBarBackButtonHidden(true). It hides it but the space taken by back button was still there.
My code is similar to this:
struct FirstScreen: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: SecondScreen()) {
Text("Go to Second Screen")
}
}
}
}
}
struct SecondScreen: View {
var body: some View {
VStack {
NavigationLink(destination: ThirdScreen()) {
Text("Go to Third Screen")
}
}
}
}
struct ThirdScreen: View {
var body: some View {
VStack {
NavigationLink(destination: FirstScreen()) {
Text("Go to First Screen")
}
}
}
}
this is the image
You're pushing the FirstScreen onto your navigation stack, but FirstScreen contains its own NavigationView. If you really want to keep pushing them on the stack, then move the NavigationView outside of FirstScreen.
struct ContentView: View {
var body: some View {
NavigationStack { // Use NavigationStack for iOS 16
FirstScreen()
}
}
}
struct FirstScreen: View {
var body: some View {
VStack {
NavigationLink(destination: SecondScreen()) {
Text("Go to Second Screen")
}
}
}
}
If you actually want to pop all the views off the stack and go back to FirstScreen you should use
init(path: Binding<NavigationPath>, etc)
Here's a simple example passing the path down the stack and resetting it to pop back to the root…
enum Screen {
case two, three
}
struct ContentView: View {
#State var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
VStack {
// value gets appended to path
NavigationLink("Go to Second Screen", value: Screen.two)
}
// Decides which screen to show for values in path
.navigationDestination(for: Screen.self) { index in
switch index {
case .two:
SecondScreen(path: $path)
case .three:
ThirdScreen(path: $path)
}
}
}
}
}
struct SecondScreen: View {
#Binding var path: NavigationPath
var body: some View {
VStack {
NavigationLink("Go to Third Screen", value: Screen.three)
}
}
}
struct ThirdScreen: View {
#Binding var path: NavigationPath
var body: some View {
VStack {
Button("Go to First Screen") {
// reset the path to pop to root
path = NavigationPath()
}
}
}
}

SwiftUI Show navigation bar title on the back button but not in the previous View

I have two views, one leads to the other. I want that the second view uses the title of the first view for the back button, which should then be: "<View1".
I don't want to show the title in the first view.
Problem: I can't hide navigation bar because it will also hide a custom button which is within it. Setting .navigationTitle("") hides the title in the first view, but also hides it from the back button in the second view.
What I have now:
What I would like to have:
Code:
struct ContentView: View {
#State var isLinkActive = false
var body: some View {
NavigationView {
VStack {
NavigationLink("go to the second view", destination: SecondView(), isActive: $isLinkActive).navigationTitle("View1")
.navigationBarItems(leading: Button(action: {
()
}, label: {
Text("custom button")
}))
}
}.navigationViewStyle(StackNavigationViewStyle())
}
private func btnPressed() {
isLinkActive = true
}
}
struct SecondView: View {
var body: some View {
Color.blue
}
}
You need to create custom back button for destination view as well,and you shouldn’t set navigation title for navigationLink, that’s why you are not able to hide “View1” correctly.
Check below code.
import SwiftUI
struct Test: View {
#State var isLinkActive = false
var body: some View {
NavigationView {
VStack {
NavigationLink("go to the second view", destination: SecondView()
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: Button(action: {
isLinkActive = false
}, label: {
HStack{
Image(systemName: "backward.frame.fill")
Text("View1")
}
})) ,
isActive: $isLinkActive)
}.navigationBarItems(leading: Button(action: {
()
}, label: {
Text("custom button")
}))
}.navigationViewStyle(StackNavigationViewStyle())
}
private func btnPressed() {
isLinkActive = true
}
}
struct SecondView: View {
var body: some View {
Color.blue
}
}
You can try and make navigationBar code as reusable component, because you might need to do this at multiple places.
Output-:
I achieved this by using two modifiers on my main view. Similar to your case, I didn't want a title on the first view, but I wanted the back button on the pushed view to read < Home, not < Back.
.navigationTitle("Home")
.toolbar {
ToolbarItem(placement: .principal) {
Text("")
}
}

How to replace the current view in SwiftUI?

I am developing an app with SwiftUI.
I have a NavigationView and I have buttons on the navigation bar. I want to replace the current view (which is a result of a TabView selection) with another one.
Basically, when the user clicks "Edit" button, I want to replace the view with another view to make the edition and when the user is done, the previous view is restored by clicking on a "Done" button.
I could just use a variable to dynamically choose which view is displayed on the current tab view, but I feel like this isn't the "right way to do" in SwiftUI. And this way I could not apply any transition visual effect.
Some code samples to explain what I am looking for.
private extension ContentView {
#ViewBuilder
var navigationBarLeadingItems: some View {
if tabSelection == 3 {
Button(action: {
print("Edit pressed")
// Here I want to replace the tabSelection 3 view by another view temporarly and update the navigation bar items
}) {
Text("Edit")
}
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
TabView(selection: $tabSelection) {
ContactPage()
.tabItem {
Text("1")
}
.tag(1)
Text("Chats")
.tabItem() {
Text("2")
}
.tag(2)
SettingsView()
.tabItem {
Text("3")
}
.tag(3)
}.navigationBarItems(leading: navigationBarLeadingItems)
}
}
}
Thank you
EDIT
I have a working version where I simply update a toggle variable in my button action that makes my view display one or another thing, it is working but I cannot apply any animation effect on it, and it doesn't look "right" in SwiftUI, I guess there is something better that I do not know.
If you just want to add animations you can try:
struct ContentView: View {
...
#State var showEditView = false
var body: some View {
NavigationView {
TabView(selection: $tabSelection) {
...
view3
.tabItem {
Text("3")
}
.tag(3)
}
.navigationBarItems(leading: navigationBarLeadingItems)
}
}
}
private extension ContentView {
var view3: some View {
VStack {
if showEditView {
FormView()
.background(Color.red)
.transition(.slide)
} else {
Text("View 3")
.background(Color.blue)
.transition(.slide)
}
}
}
}
struct FormView: View {
var body: some View {
Form {
Text("test")
}
}
}
A possible alternative is to use a ViewRouter: How To Navigate Between Views In SwiftUI By Using An #EnvironmentObject.

Navigate to same view in SwiftUI

So I'm trying to navigate from one view to another in SwiftUI and have stumbled upon a problem.
My views looks like this and trying to navigation from one DetailView to another DetailView but as soon I push the navigation link I'm forced back to the first DetailView.
Any ideas on how to achieve navigation to the same view in SwiftUI?
ListView.swift
struct ListView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DetailView(data: data)) {
Text("Navigate")
}
}
}
}
}
DetailView.swift
struct DetailView: View {
var body: some View {
VStack {
NavigationLink(destination: DetailView(data: otherData)) {
Text("Navigate")
}
}
}
}
EDIT:
Remove the NavigationView in DetailView as pointed out below but still facing the same issue.
Remove NavigationView from DetailView, there should be only one in stack, so when you navigate all new links are opened in original NavigationView:
struct DetailView: View {
var body: some View {
VStack {
NavigationLink(destination: DetailView(data: otherData)) {
Text("Navigate")
}
}
}
}

Navigation of Pop and Push not working in SwiftUI?

I have two screens. First screen has following code
NavigationLink(destination: SecondScreen()) {
Text("Scan Qr Code")
}
The NavigationLink navigates to second screen which works as expected. In the second screen
struct SecondScreen: View {
#Environment(\.presentationMode) var presentation
var body: some View{
Button("Done") {
self.presentation.wrappedValue.dismiss()
}
}}
The button in second screen pops to first screen which works as expected, but as soon as I come back to first screen and I click NavigationLink, it does not move to second screen.
Here is a solution. Use a button on the first screen instead of the navigation link and use that button to pop out a sheet. I tested this and it worked for me!
struct ContentView: View {
#State private var showingAddExpense = false
var body: some View {
NavigationView{
Button(action: {
self.showingAddExpense = true
}) {
Text("Button")
}
.sheet(isPresented: $showingAddExpense) {
SecondScreen()
}
}
}
}
struct SecondScreen: View {
#Environment(\.presentationMode) var presentation
var body: some View{
NavigationView{
Button("Done") {
self.presentation.wrappedValue.dismiss()
}
}
}
}

Resources