I want to have a TabView inside a NavigationView. The reason is that I am showing a List inside the TabView. When a user taps on an item in the list, the list uses a NavigationLink to show a detailed screen.
The problem is that the navigationBarTitle is now broken. It does not animate when the user scrolls through the items (sometimes). I don't want to wrap my NavigationView inside the TabView, since that will always show the TabView. I don't want it.
This is the reproduction code. If you run this in the simulator, the animation will break when switch a few times between the tabs and you scroll through the list. You will see that the navigation bar will remain where it is, without the animation.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
TabView {
TestView()
.tabItem {
Image(systemName: "person.3")
}
TestView()
.tabItem {
Image(systemName: "person.2")
}
}
.navigationTitle("Test")
.navigationViewStyle(.stack)
}
}
}
struct TestView: View {
var body: some View {
List {
Text("test")
}
.listStyle(.plain)
}
}
Related
Currently I am working on SwiftUI project. I want to hide the build-in navbar. For this purpose I have to add these lines,
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
to each of view before pushing it into navigation controller in SwiftUI.
NavigationLink(destination:
ForgotPasswordView()
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
) {
Text("Forgot Password?")
.foregroundColor(.white)
}
Same will be done for LoginView
NavigationLink(destination:
LoginView()
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
) {
Text("Login")
.foregroundColor(.white)
}
So I need any generic method like we did in storyboard, hide it from root view and no child will have the navbar on top.
Updated for iOS 16+
You can wrap the view by creating a new view that accepts a child view, which adds all the necessary modifiers so that you don't have to repeat it every time.
struct NavigationWrapper<Content: View>: View {
#ViewBuilder var childView: Content
var body: some View {
childView
.toolbar(.hidden)
}
}
Now, you can use that wrapper for your navigation link's destination.
Here is an example:
struct ContentView: View {
var body: some View {
NavigationStack {
List {
ForEach(0..<10) { i in
NavigationLink("Row \(i)") {
NavigationWrapper {
Text("Detailed view for \(i)th row.")
}
}
}
}
.navigationTitle("RowGap")
.toolbar(.hidden)
}
}
}
It hides out the navigation bar and toolbar completely.
The NavigationViews inside my PageTabView are wrong aligned on first appear.
When i scroll to another page on my PageTabView and go back to the first page, the alignment is correct.
The content of the navigationview (red) is beneath the navigationbar on first appear.
Image of first appearance of the NavigationView
Image of the second appearance of the NavigationView
struct ContentView: View {
var body: some View {
TabView {
ForEach(0..<3) { index in
NavigationView {
Color.red
.navigationTitle("\(index). Page")
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
.tabViewStyle(.page)
}
}
Edit:
I want to build the layout of the rooms tab in the apple home app in compact mode. Therefore the TabView in the code above gets wrapped in another tabview without the pagetabviewstyle modifier.
This works, but the same extracted problem in the code above happens.
var body: some View {
NavigationView {
TabView {
ForEach(0..<3) { index in
Color.red
.navigationTitle("\(index). Page")
}
}
.tabViewStyle(.page)
}
}
Just moved NavigationView outside, it fixed the problem
I'm just picking up SwiftUI after a long break but I don't understand why I can't place a Navigation View within a Tab View.
I want my Navigation View to be a .tabItem so the view appears as part of the main app navigation so I'm trying this :
struct ContentView: View {
var body: some View {
TabView {
NavigationView {
.tabItem {
Text("Home")
}
Text("Tab bar test")
.navigationBarTitle("Page One")
}
}
This doesn't work with error message
Cannot infer contextual base in reference to member 'tabItem'
But when I try this
var body: some View {
TabView {
NavigationView {
Text("Tab bar test")
.navigationBarTitle("Page One")
}
}
.tabItem {
Image(systemName: "1.circle")
Text("Home")
}
}
It builds fine but the tab bar doesn't show up.
My primary question which I'm hoping would be useful to others, is ...
Why can't I make a make a Navigation View a tab bar item by nesting .tabItem directly inside the Navigation View (as per my first example)?
I think it's a similar question to this one but there's no code there. And then quite similar to this one but they seem to be using .tabItem directy with ContentView like I want to with NavigationView but that isn't building!?
I'm probably misunderstanding something simple but I don't get this at all at the moment.
Do this for a better overview:
ContentView:
struct ContentView : View {
var body: some View {
TabView {
FirstView()
.tabItem {
Image(systemName: "folder.fill")
Text("Home")
}
SecondView()
.tabItem {
Image(systemName: "folder.fill")
Text("SecondView")
}
}
}
}
FirstView
struct FirstView: View {
var body: some View {
NavigationView {
Text("FirstView")
.navigationBarTitle("Home")
}
}
}
}
SecondView
struct SecondView: View {
var body: some View {
NavigationView {
Text("SecondView")
.navigationBarTitle("Home")
}
}
}
}
.tabItem should be used as a modifier on the view that represents that tab.
In the first example, this doesn't work because of a syntax error -- you're trying to use it on the opening of a closure in NavigationView {, when instead you want it on the outside of the closing brace: NavigationView { }.tabItem(...)
In the second example, you're using .tabItem as a modifier on the entire TabView instead of the NavigationView.
Both of your examples may have revealed what was going on more obviously if you indent your code so that you can see the hierarchy. Trying selecting your code in Xcode and using Ctrl-I to get Xcode to properly format it for you.
Here's a working version:
struct ContentView : View {
var body: some View {
TabView {
NavigationView {
Text("Tab bar test")
.navigationBarTitle("Page One")
}
.tabItem { //note how this is modifying `NavigationView`
Image(systemName: "1.circle")
Text("Home")
}
}
}
}
This sounds a bug in SwiftUI's NavigationView and TabView, when I have a TabView with (let's say) 2 tabs, the first tab has a TextField and the second tab has a NavigationView, follow these steps to produce the bug:
show the keyboard by tapping on the text field.
press on the "Return" button to hide the keyboard.
go to tab 2.
notice the weird bottom added space below the view, which approximately equals the height of the keyboard.
-Note1: if you do any of the following the bug won't be reproduced:
once the app launches, open tab 2, return to tab 1 and show the keyboard.
remove NavigationView from tab 2
show the keyboard one more time in tab 1
Note2:
I use GeometryReader in tab 2 to show the whole view area by a yellow color.
working code sample (just copy-paste it to try):
struct ContentView: View {
var body: some View {
TabView {
View1()
.tabItem { Text("View1") }
.tag(1)
View2()
.tabItem { Text("View2") }
.tag(2)
}
}
}
struct View1: View {
#State private var myText = ""
var body: some View {
VStack {
Text("this is view 1")
TextField("Enter Value", text: $myText)
}
}
}
struct View2: View {
var body: some View {
NavigationView {
GeometryReader { reader in
Text("this is view 2")
.onAppear{
print("view 2 on appear")
}
}
.background(Color.yellow)
}
}
}
screenshot:
Is there a way to workaround this problem without having to remove the NavigationView, I tried every possible solution but couldn't a find a clue to avoid it ?
I'm a bit confused on how navigation works in SwiftUI. Does only the view starting the navigation need a NavigationView? I have one view with a NavigationView that has a NavigationLink to a second view. The second view then has a NavigationLink to a third and final view.
However, when my second view navigates to my third view, I get this message in the logs:
unbalanced calls to begin/end appearance transitions for <_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_: 0x7f85d844bf90>.
I don't know if I'm handling navigation through multiple screens correctly and I'm getting some really odd behavior where pressing Next on my second screen takes me back to my first somehow...
//This is the link in my first view, my seconds link is the same except it does to my next step and the tag is different
NavigationLink(
destination: PasswordView(store: self.store),
tag: RegisterState.Step.password,
selection: .constant(store.value.step)
)
Navigation is a little bit tricky in SwiftUI, after creating one navigationview you don't need to create again in your 2nd or 3rd view. I am not sure how you creating it firstly. Here is an example how navigation is working.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: SecondView()) {
Text("Show Second View")
}.navigationBarTitle("FirstView", displayMode: .inline)
}
}
}
}
struct SecondView: View {
var body: some View {
NavigationLink(destination: ThirdView()) {
Text("Show Third view")
}.navigationBarTitle("SecondView", displayMode: .inline)
}
}
struct ThirdView: View {
var body: some View {
Text("This is third view")
.navigationBarTitle("ThirdView", displayMode: .inline)
}
}