NavigationView inside TabView going to next page by default - swiftui

I am wrapping my navigaton view inside a tab view.
but when I load the app, my navigation view is always going to its next page by default.
How can I stop this ? I spent so much time but could not figure it out.
here is my view
ZStack {
Color.red
.edgesIgnoringSafeArea(.all)
VStack {
TabView(selection:$selectedTab) {
NavigationView {
VStack {
Text("Hello")
}
.edgesIgnoringSafeArea(.all)
}
.tag(1)
VStack {
Text("Two")
}
.tag(2)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}
FirstView is a navigation view like this
NavigationView {
VStack {
...
}
.sheet(isPresented: $myForm, onDismiss:{ }) {
NewFormView(...)
}
.navigationBarHidden(true)
}
It appears like this

Related

NavigationLink in the Page TabView works with an error

Here is the code i am trying to reproduce.
The problem is that when you click on the link, the screen opens without animation and an error is generated in the console.
But if you go to another page Tab and go back, then everything works correctly.
var body: some View {
TabView {
//First Screen
NavigationView {
NavigationLink {
Text("Hello Page 1")
} label: {
Text("Page 1")
}
}
.navigationViewStyle(.stack)
.tag(1)
//Second Screen
NavigationView {
NavigationLink {
Text("Hello Page 2")
} label: {
Text("Page 2")
.foregroundColor(.green)
}
}
.navigationViewStyle(.stack)
.tag(2)
}
.tabViewStyle(.page(indexDisplayMode: .always))
}
To correct this error you need to use individual structs in your TabView. In a real app, you would be doing this as a matter of course, and would not ever see this error. An example would be:
struct MyTabView: View {
var body: some View {
TabView {
Content1()
.tag(1)
Content2()
.tag(2)
}
.tabViewStyle(.page(indexDisplayMode: .always))
}
}
struct Content1: View {
var body: some View {
NavigationView {
NavigationLink {
Text("Hello Page 1")
} label: {
Text("Page 1")
}
}
.navigationViewStyle(.stack)
}
}
struct Content2: View {
var body: some View {
NavigationView {
NavigationLink {
Text("Hello Page 2")
} label: {
Text("Page 2")
}
}
.navigationViewStyle(.stack)
}
}
NavigationView would go in each of these contained structs as they would be the top of the hierarchy for each tab.

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).

SwiftUI: Hidden NavigationBar blocks UI

I am using a NavigationView to navigate between views. As I don't want to use the NavigationBar, I am hiding it. Unfortunately, the area where the NavigationBar was (it is hidden) is still blocking the UI. A button there cannot be tapped.
How can I fix this?
// First View
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Spacer()
Text("Title")
Spacer()
NavigationLink(destination: View2()) {
Text("Next")
}
.navigationBarTitle("", displayMode: .inline)
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
Spacer()
}
}
}
}
and
// Second View: Here the button cannot be tapped
struct View2: View {
var body: some View {
NavigationView {
VStack {
Button(action: {
print("this button doesn't work")
}, label: {
Text("Do something")
})
Spacer()
}
.padding(.top, 50)
.edgesIgnoringSafeArea(.all)
}
.navigationBarTitle("", displayMode: .inline)
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
}
The issue is due to used second NavigationView - it is wrong, there should be only one root navigation view which manages navigation stack in this case.
So here is fixed view (child one)
struct View2: View {
var body: some View {
VStack {
Button(action: {
print("this button doesn't work")
}, label: {
Text("Do something")
})
Spacer()
}
.padding(.top, 50)
.edgesIgnoringSafeArea(.all)
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
}
Tested with Xcode 13.2 / iOS 15.2
Note: as you hide navigation but you don't need mode modifier as well, so removed .navigationBarTitle("", displayMode: .inline). Just for your info.

SwiftUI TabView brightness views vertical location

I'm trying to change the brightness of a TabView in SwiftUI, but whenever the brightness toggles, the sub-views change their vertical position. This is highly mysterious to me, as it seems strange for brightness to have any influence at all on the position of views.
If you've been following my past few questions, you might recognize that I want to change the brightness because I want to dim the background view as a new view slides up (on the press of a button). That's why, in the sample code, we must include the Z-Stack.
Sample Code:
struct ContentView: View {
#State var press: Bool = Bool()
var body: some View {
ZStack {
TabView {
NavigationView {
Button(action: { press.toggle() }) {
Text("Toggle")
}
}
.tabItem {
Image(systemName: "square.stack").font(.title)
Text("View One")
}
NavigationView {
Button(action: { press.toggle() }) {
Text("Toggle")
}
}
.tabItem {
Image(systemName: "checkmark.square")
Text("View Two")
}
}
.brightness(press ? -0.1: 0)
}
}
}
The issue is with the multiple NavigationViews that are inside the TabView You really are supposed to have only 1 NavigationView at the root of the view hierarchy. Using multiple NavigationViews is discouraged and can lead to unexpected results, like this. Therefore, the inside of the body should be:
NavigationView {
ZStack {
TabView {
Button(action: { press.toggle() }) {
Text("Toggle")
}
.tabItem {
Image(systemName: "square.stack")
Text("View One")
}
Button(action: { press.toggle() }) {
Text("Toggle")
}
.tabItem {
Image(systemName: "checkmark.square")
Text("View Two")
}
}
.brightness(press ? -0.1: 0)
}
}

SwiftUI 2.0: Close button is not dismissing the view - how do I get the Close button to return to the previous view?

I have tried to use Buttons and Navigation Links from various examples when researched on this channel and on the net. The NavigationLink would be ok, except that the NavigationView is pushing everything down in my view.
I have a view that contains an image and a text like this: ( x Close) but when I use the code below, the Close button is not doing anything.
In ContentView() I have a (?) button that takes me from WalkthroughView(), then to the PageTabView, then to this view, TabDetailsView:
ContentView():
ZStack {
NavigationView {
VStack {
Text("Hello World")
.padding()
.font(.title)
.background(Color.red)
.foregroundColor(.white)
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
withAnimation {
showOnBoarding = true
}
} label: {
Image(systemName: "questionmark.circle.fill")
}
}
}
}
.accentColor(.red)
.disabled(showOnBoarding)
.blur(radius: showOnBoarding ? 3.0 : 0)
if showOnBoarding {
WalkthroughView(isWalkthroughViewShowing: $isWalkthroughViewShowing)
}
}
.onAppear {
if !isWalkthroughViewShowing {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation {
showOnBoarding.toggle()
isWalkthroughViewShowing = true
}
}
}
}
WalkthroughView():
var body: some View {
ZStack {
GradientView()
VStack {
PageTabView(selection: $selection)
// shows Previous/Next buttons only
ButtonsView(selection: $selection)
}
}
.transition(.move(edge: .bottom))
}
PageTabView():
var body: some View {
TabView(selection: $selection) {
ForEach(tabs.indices, id: \.self) { index in
TabDetailsView(index: index)
}
}
.tabViewStyle(PageTabViewStyle())
}
below, is the TabDetailsView():
At the top of the view is this Close button, when pressed, should send me back to ContentView, but nothing is happening.
struct TabDetailsView: View {
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
let index: Int
then, inside the body:
VStack(alignment: .leading) {
Spacer()
VStack(alignment: .leading) {
// Button to close each walkthrough page...
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image(systemName: "xmark.circle.fill")
Text("Close")
}
.padding(.leading)
.font(.title2)
.accentColor(.orange)
Spacer()
VStack {
Spacer()
Image(tabs[index].image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 415)
.padding(.leading, 10)
Text(tabs[index].title)
.font(.title)
.bold()
Text(tabs[index].text)
.padding()
Spacer()
}
.foregroundColor(.white)
}
}
if showOnBoarding {
WalkthroughView(isWalkthroughViewShowing: $isWalkthroughViewShowing)
}
Inserting view like above is not a presentation in standard meaning, that's why provided code does not work.
As this view is shown via showOnBoarding it should be hidden also via showOnBoarding, thus the solution is to pass binding to this state into view where it will be toggled back.
Due to deep hierarchy the most appropriate way is to use custom environment value. For simplicity let's use ResetDefault from https://stackoverflow.com/a/61847419/12299030 (you can rename it in your code)
So required modifications:
if showOnBoarding {
WalkthroughView(isWalkthroughViewShowing: $isWalkthroughViewShowing)
.environment(\.resetDefault, $showOnBoarding)
}
and in child view
struct TabDetailsView: View {
#Environment(\.resetDefault) var showOnBoarding
// .. other code
Button(action: {
self.showOnBoarding.wrappedValue.toggle()
}) {
Image(systemName: "xmark.circle.fill")
Text("Close")
}