I am having trouble with navigation in SwiftUI. I have a button on a navigation bar, if clicked it pushes a new navigation view with list of items. When one of those items is tapped, it pushes a detail view.
But I am ending up with something like this.
Below is the code
struct FirstView: View {
var body: some View {
NavigationView {
List {
...
}
.navigationBarTitle(Text("First View"))
.navigationBarItems(trailing: MyButton())
}
}
}
struct MyButton: View {
var body: some View {
NavigationLink("SecondView", destination: SecondView())
}
}
struct SecondView: View {
var body: some View {
NavigationView {
Text("My View")
}
}
}
Remove the NavigationView from SecondView.
The NavigationLink puts the second view inside the first views navigations view, so you do not need to put it inside a second one.
You can still update the title of the view from SecondView like so:
struct SecondView: View {
var body: some View {
Text("My View")
.navigationBarTitle("Second View")
}
}
Quinn is right.
but if You don't want a big area above:
add:
struct SecondView: View {
var body: some View {
Text("My View")
.navigationBarTitle("Second View", displayMode: .inline)
}
}
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.
I'm new in SwiftUI and I have come to a weird behavior that is driving me nuts. I'm using the following code as an example:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
ScrollView {
NavigationLink(
destination: NonEmojiView(),
label: { Text("NON-emoji view.") }
)
Spacer()
NavigationLink(
destination: EmojiView(),
label: { Text("Emoji view.") }
)
}
.navigationTitle("I love emojis")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct EmojiView: View {
var body: some View {
Text("Testing emojis in navigation titles.")
.navigationTitle("♥️")
}
}
struct NonEmojiView: View {
var body: some View {
Text("Testing emojis in navigation titles.")
.navigationTitle("Boring title")
}
}
When I run this app (iOS 14, XCode 12.2) and tap on the Emoji view (second navigation link), and then the "< Back" button in the navigation bar, the inline title style is shown in the navigation bar (image1) instead of the expected one (image 2). Any clue why? Anything am I doing wrong? Is that a SwiftUI bug? The only difference is the emoji in the title.
EDIT: As pointed out by Luffy, if the navigationTitle in ContentView contains an emoji too, it works well. May be related to the height of the title? More points for this to be a SwiftUI bug.
Thanks!!
If you add emoji in a ContentView's navBarTitle, it will work. If you remove it, then it won't. I think it's a SwiftUI bug.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
ScrollView {
NavigationLink(
destination: NonEmojiView(),
label: { Text("NON-emoji view.") }
)
Spacer()
NavigationLink(
destination: EmojiView(),
label: { Text("Emoji view.") }
)
}
.navigationBarTitle(Text("I ❤️ emojis"), displayMode: .large) // <~ HERE
}
}
}
struct EmojiView: View {
let emoji = Image(systemName: "heart.fill")
var body: some View {
Text("Testing emojis in navigation titles.")
.navigationBarTitle(Text("❤️"), displayMode: .large)
}
}
struct NonEmojiView: View {
var body: some View {
Text("Testing emojis in navigation titles.")
.navigationBarTitle(Text("Boring title"), displayMode: .large)
}
}
I have a Content file and am hiding the navigation bar because it takes up space and pushes elements down. One of the buttons in the ContentView redirects (using a navigation link) to another view. In this other view, the navigationBar is still hidden....for simplicity sake, I'll cut out some of the code from ContentView:
//this is the view that looks "fine" (i.e. the navigation bar takes up no space)
struct ContentView: View {
#State private var isPresentedSettings = false
var body: some View {
NavigationView {
ZStack {
VStack {
SettingsButton(isPresentedSettings: $isPresentedSettings)
}
}.navigationBarTitle("").navigationBarHidden(true)
}
}
}
//this is the button that pulls up the settings page view
struct SettingsButton: View {
#Binding var isPresentedSettings: Bool
var body: some View {
NavigationLink (destination: SettingsPageView(isPresentedSettings:
self.$isPresentedSettings)) {
Button(action: { self.isPresentedSettings.toggle() }, label: { Text("Button") })
}
}
}
//This is the view that should have a navigationbar but it doesn't
struct SettingsPageView: View {
#Binding var isPresentedSettings: Bool
var body: some View {
NavigationView {
VStack {
Text("This is a view")
}.navigationBarTitle("Settings", displayMode: .inline)
}
}
}
Also...there may have been typos because I just copied the code over from another computer. Sorry and thank you in advance!
Firstly, you don't need to have this isPresentedSettings variable for presenting a NavigationLink.
NavigationLink(destination: SettingsPageView()) {
Text("Button")
}
And there should be only one NavigationView in your view hierarchy.
This is how your final code can look like:
struct ContentView: View {
#State private var navBarHidden = true
var body: some View {
NavigationView {
ZStack {
VStack {
SettingsButton(navBarHidden: $navBarHidden)
}
}
.navigationBarHidden(navBarHidden)
}
}
}
struct SettingsButton: View {
#Binding var navBarHidden: Bool
var body: some View {
NavigationLink(destination: SettingsPageView(navBarHidden: $navBarHidden)) {
Text("Show View")
}
}
}
struct SettingsPageView: View {
#Binding var navBarHidden: Bool
var body: some View {
VStack {
Text("This is a view")
}
.navigationBarTitle("Settings", displayMode: .inline)
.onAppear {
self.navBarHidden = false
}
.onDisappear {
self.navBarHidden = true
}
}
}
I'm currently trying to present a modal view by pressing a button in the context menu. This works, but the code which should present the modal view is called twice and this is a problem, because I'm calling some networking requests.
Here is my currently demo project (without networking stuff):
This is the view which is called on app start.
struct ContentView: View {
#State var isModal: Bool = false
var body: some View {
Group {
Text("Main view")
}.contextMenu {
Button("Present Detail") { self.isModal = true }.sheet(isPresented: $isModal) {
DetailView()
}
}
}
}
This is the simple detail view
struct DetailView: View {
var body: some View {
Text("Detail View")
}
}
So if I place a breakpoint at the line where the DetailView() is instantiated, I see that this part is called twice. Is there a better solution to present the modal view, without being instantiated multiple times?
Use instead
var body: some View {
Group {
Text("Main view")
}.contextMenu {
Button("Present Detail") { self.isModal = true }
}.sheet(isPresented: $isModal) {
DetailView()
}
}
I have a modal sheet that is presented from my home view as such:
Button(action: {
...
}) {
...
}
.sheet(isPresented: ...) {
MySheetView()
}
In MySheetView, there is a NavigationView and a NavigationLink to push another view onto its view stack (while I'm on MySheetView screen and use the view inspector, there's only one UINavigationController associated with it which is what I expect).
However, as soon as I get to my next view that is presented from MySheetView using the NavigationLink, and I use the view hierarchy debugger, there are TWO UINavigationControllers on-top of each other. Note, this view does NOT have a NavigationView inside it, only MySheetView does.
Does anyone know what's going on here? I have a feeling this is causing some navigation bugs im experiencing. This can be easily reproduced in an example app with the same structure.
Ex:
// These are 3 separate SwiftUI files
struct ContentView: View {
#State var isPresented = false
var body: some View {
NavigationView {
Button(action: { self.isPresented = true }) {
Text("Press me")
}
.sheet(isPresented: $isPresented) {
ModalView()
}
}
}
}
struct ModalView: View {
var body: some View {
NavigationView {
NavigationLink(destination: FinalView()) {
Text("Go to final")
}
}
}
}
struct FinalView: View {
var body: some View {
Text("Hello, World!")
}
}
I don't observe the behaviour you described. Used Xcode 11.2. Probably you need to provide your code to find the reason.
Here is an example of using navigation views in main screen and sheet. (Note: removing navigation view in main screen does not affect one in sheet).
import SwiftUI
struct TestNavigationInSheet: View {
#State private var hasSheet = false
var body: some View {
NavigationView {
Button(action: {self.hasSheet = true }) {
Text("Show it")
}
.navigationBarTitle("Main")
.sheet(isPresented: $hasSheet) { self.sheetContent }
}
}
private var sheetContent: some View {
NavigationView {
VStack {
Text("Properties")
.navigationBarTitle("Sheet")
NavigationLink(destination: properties) {
Text("Go to Inspector")
}
}
}
}
private var properties: some View {
VStack {
Text("Inspector")
}
}
}
struct TestNavigationInSheet_Previews: PreviewProvider {
static var previews: some View {
TestNavigationInSheet()
}
}