SwiftUI NavigationView nested bar issue - ios

I have a View with a list with navigationLinks, and show a list of items to DetailView, and DetailView can go to another View and so on. Therefore it will have a full navigation stack in here.
However, in our app, we have two ways to show this view, first, a presenting View to show this view. Second, by other navigationLink under another NavigationView to push to this view. In the first case, it is fine. however in second case, it will show nested navigation bar, which I don't really like it.
Is there any possible way to show the following view without any nested navigationBar, in the pushing and presenting(UIKit wording) way
var body: some View {
NavigationView {
List(items) { item in
NavigationLink(destination: DetailView(item)) {
ItemRow(item)
}
}
.navigationBarTitle("Item list")
}
}
thanks a lot!

As Asperi says, you must separate the View into two Views. You have double NavigationBar because in the case where you already enter with the previous NavigationView at the top level of the View hierarchy, having another NavigatioView makes it double the NavigationBar.
You must do something like this in the View before this one:
var body: some View {
VSTack {
Text("Hello Word")
Button(action: {
if isShowWithPresent { // Some Bool
NavigationView {
SomeView()
}
} else {
AnotherView() // Some View without the NavigationView in the code
}
}) {
Text("Hit Me!")
}
}
}

Related

Force one NavigationLink to the detail view in SwiftUI

I have a master-(primary)-detail setup in my app, where most of the list items in the top-level NavigationView go to another list of submenu items, with .isDetailLink(false), and then those submenu items then navigate to the detail view. This is all working as intended.
I'm trying to also put in a NavigationLink in the top level list to got to my settings page, which I want to force into the detail view. There isn't a submenu for this link, so I tried to navigate to it directly and force .isDetailView(true). However, this causes the view to open in what would be the primary window above, but hidden at first. The detail window only has a back button, which then makes the intended view pop out from the menu column.
Is there a way to force only the settings menu item to open in the detail view, essentially skipping one navigation level?
Thanks!
Here's the simplified version of what I'm currently trying:
struct ContentView: View {
var body: some View {
NavigationView {
List {
NavigationLink(destination: SubMenu1()) {Text("MenuItem1")}
.isDetailLink(false)
NavigationLink(destination: SubMenu2()) {Text("MenuItem2")}
.isDetailLink(false)
NavigationLink(destination: SubMenu3()) {Text("MenuItem3")})
.isDetailLink(false)
NavigationLink(destination: SettingsView()) {Text("Settings")}
.isDetailLink(true)
}
}
}
}
struct SubMenu: View {
var body: some View {
List {
ForEach(menuItems, id: \.self) { item in
NavigationLink(destination: DetailView(item)) {
Text(item)
}
}
}
}
}
UPDATE
I've taken some screenshots to illustrate the issue:
The top level navigation view:
With one of the sub-menus selected, and .isDetailView(false). This is working properly:
Settings does not have a sub-menu like the others, so I want it to open directly on the right hand side. Instead, it opens like this, and is only revealed when the back button is pressed:
Hey, I have seen your screenshots. I Think you are using NavigationView 2 times there in Settings. Please remove any one of them, It should work fine!

Does SwiftUI have a built-in, stack-based navigator that does not require a list layout?

I want a way to have stack-based navigation in SwiftUI. Whenever I try to look up how to do that, I get information about NavigationView. However, it looks like NavigationView is intended to be used to display a list where each entry navigates to a page when tapped. Is there way to have stack-based navigation like that of NavigationView without having to conform to a list structure?
So, NavigationView is used to enable navigation to other views. This can be used with any type of view. The following example will show two screens: the ContentView and the DetailView.
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Text("Main View")
NavigationLink("Go to Detail", destination: DetailView())
}
.navigationBarTitle("Content View")
}
}
}
struct DetailView: View {
var body: some View {
Text("Detail body")
.navigationBarTitle("Detail")
}
}
If you press on the 'Go to Detail' Navigation Link, then SwiftUI pushes the destination view onto the screen. Pressing the back button on the detail view will pop the current view and return to the ContentView. You can modify the NavigationView with titles and buttons using some modifiers, but note that the .navigationBarTitle() modifier has to be used on the inside of the NavigationView, not the outside.
In summary, the NavigationView can be used with any type of view and a List isn't required.

Why does NavigationView have to be a top-level view?

I am new to iOS and SwiftUI - why does NavigationView even exist? Why isn't there just NavigationLinks whenever we need to link somewhere?
And why does NavigationView have to be the top level view? Semantically it just doesn't make sense as the top level View should be VStack, Zstack etc
Currently:
struct ContentView : View {
var body: some View {
NavigationView {
VStack {
Text("Hello World")
NavigationLink(destination: DetailView()) {
Text("Do Something")
}
}
}
}
}
Should be:
struct ContentView : View {
var body: some View {
VStack {
Text("Hello World")
NavigationView {
NavigationLink(destination: DetailView()) {
Text("Do Something")
}
}
}
}
}
Any idea?
SwiftUI gives you a whole bunch of conveniences practically for free.
One of those is an interface building block called NavigationView.
That interface building block has some convenient features and abilities, such as giving you automatic navigation interface (like back buttons to the original View in the navigation bar of the view you followed via a NavigationLink, etc).
Most of those conveniences simply wouldn’t make sense if it wasn’t the top level of your view.
Is there something you wanted from NavigationView at a lower level? I can’t imagine what, but I suspect there’s another SwiftUI building block that will achieve it, made specifically for the context.

SwiftUI: navigation bar shows title for first list item, otherwise shows "Back"

I have basic list and detail views set up as shown below. The problem is that the first detail view shows "< Inbox" as the back button while the other rows show "< Back". I've tried various options and places for .navigationBarTitle but haven't found any way to solve this.
Main / list view:
var body: some View {
NavigationView {
List {
ForEach(userData.message) { messageSection in
Section(header: Text(messageSection.id)) {
ForEach(messageSection.messages) { message in
NavigationLink(destination: MessageDetail(message: message)) {
MessageRow(message: message)
}
}
}
}
}.navigationBarTitle(Text("Inbox"), displayMode: .inline)
}
}
Detail view:
var body: some View {
ScrollView {
VStack {
...
}
}.navigationBarTitle(Text("\(self.message.title) (\(self.message.preview))"), displayMode: .inline)
}
When the title is too long to display in the space allocated for the left bar button, the system uses "Back" as the button title instead.

What's the equivalent of the UISplitViewController in SwiftUI

I need to implement an UI which close to the default Mail app in iPad and iPhone.
The App has two sections, typically, the master view will be displayed on the left side and detail view will be displayed in the right side in iPad.
In the phone, the master view will be displayed on whole screen, the detail view can be pushed as second screen.
How to implement it in the new SwiftUI
There is not really a SplitView in SwiftUI, but what you describe is automatically accomplished when you use the following code:
import SwiftUI
struct MyView: View {
var body: some View {
NavigationView {
// The first View inside the NavigationView is the Master
List(1 ... 5, id: \.self) { x in
NavigationLink(destination: SecondView(detail: x)) {
Text("Master\nYou can display a list for example")
}
}
.navigationBarTitle("Master")
// The second View is the Detail
Text("Detail placeholder\nHere you could display something when nothing from the list is selected")
.navigationBarTitle("Detail")
}
}
}
struct SecondView: View {
var detail: Int
var body: some View {
Text("Now you are seeing the real detail View! This is detail \(detail)")
}
}
This is also why the .navigationBarTitle() modifier should be applied on the view inside the NavigationView, instead of on the NavigationView itself.
Bonus: if you don't like the splitView navigationViewStyle, you can apply the .navigationViewStyle(StackNavigationViewStyle()) modifier on the NavigationView.
Edit: I discovered that the NavigationLink has an isDetailLink(Bool) modifier. The default value appears to be true, because by default the "destination" is presented in the detail view (on the right). But when you set this modifier to false, the destination is presented as a master (on the left).

Resources