NavigationView pops back to root, omitting intermediate view
Currently placing a NavigationLink
in the .navigationBarItems
may cause some issues.
A possible solution is to move the NavigationLink
to the view body and only toggle a variable in the navigation bar button:
struct ModelListView: View {
@State var modelViewModel = ModelViewModel()
@State var isAddLinkActive = false // add a `@State` variable
var body: some View {
List(modelViewModel.modelValues.indices) { index in
NavigationLink(
destination: ModelEditView(model: $modelViewModel.modelValues[index]),
label: {
Text(modelViewModel.modelValues[index].titel)
}
)
}
.background( // move the `NavigationLink` to the `body`
NavigationLink(destination: ModelAddView(modelViewModel: $modelViewModel), isActive: $isAddLinkActive) {
EmptyView()
}
.hidden()
)
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing: trailingButton)
}
// use a Button to activate the `NavigationLink`
var trailingButton: some View {
Button(action: {
self.isAddLinkActive = true
}) {
Image(systemName: "plus")
}
}
}
SwiftUI pushed View through Navigationview pops back after dismissing sheet
Currently placing a NavigationLink
in the navigationBarItems
may cause some issues.
Try removing the NavigationLink
from navigationBarItems
and put it the background
:
struct homeView: View {
@State var showSearch: Bool = false
var body: some View {
NavigationView {
Text("home")
// move `NavigationLink` outside `navigationBarItems`
.background(NavigationLink("", destination: SearchContentView(), isActive: $showSearch))
.navigationBarTitle("", displayMode: .inline)
.navigationViewStyle(StackNavigationViewStyle())
.navigationBarItems(trailing: HStack {
Button("search", action: {
showSearch.toggle()
})
})
}
}
}
SwiftUI NavigationView pop itself when a datasource is finished loading
This happens because you're specifying id
as item
itself, and when list updated there's no original item anymore, so it closes
If you just wanna modify items without adding/removing/reordering, you can make index your item id:
NavigationView {
List(viewModel.dataSource.indices, id: \.self) { i in
let item = viewModel.dataSource[i]
NavigationLink(destination: Text("\(item)")) {
Text("\(item)")
.padding()
}
}
}
But with more complex data you need to have your items Identifiable
with unique ids, and you won't have such problem. Check out this example:
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
NavigationView {
List(viewModel.dataSource) { item in
NavigationLink(destination: Text("\(item.value)")) {
Text("\(item.value)")
.padding()
}
}
}
}
}
class ViewModel: ObservableObject {
@Published private(set) var dataSource: [Item] = [1, 2, 3, 4, 5]
init() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [self] in // simulate calling webservice
// you're modifying value but id stays the same
self.dataSource[0].value = 99
}
}
}
struct Item: Identifiable, ExpressibleByIntegerLiteral {
let id = UUID()
var value: Int
init(integerLiteral value: IntegerLiteralType) {
self.value = value
}
}
How to remove the default Navigation Bar space in SwiftUI NavigationView
For some reason, SwiftUI requires that you also set .navigationBarTitle
for .navigationBarHidden
to work properly.
NavigationView {
FileBrowserView(jsonFromCall: URLRetrieve(URLtoFetch: applicationDelegate.apiURL))
.navigationBarTitle("")
.navigationBarHidden(true)
}
Update
As @Peacemoon pointed out in the comments, the navigation bar remains hidden as you navigate deeper in the navigation stack, regardless of whether or not you set navigationBarHidden
to false
in subsequent views. As I said in the comments, this is either a result of poor implementation on Apple's part or just dreadful documentation (who knows, maybe there is a "correct" way to accomplish this).
Whatever the case, I came up with a workaround that seems to produce the original poster's desired results. I'm hesitant to recommend it because it seems unnecessarily hacky, but without any straightforward way of hiding and unhiding the navigation bar, this is the best I could do.
This example uses three views - View1
has a hidden navigation bar, and View2
and View3
both have visible navigation bars with titles.
struct View1: View {
@State var isNavigationBarHidden: Bool = true
var body: some View {
NavigationView {
ZStack {
Color.red
NavigationLink("View 2", destination: View2(isNavigationBarHidden: self.$isNavigationBarHidden))
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {
self.isNavigationBarHidden = true
}
}
}
}
struct View2: View {
@Binding var isNavigationBarHidden: Bool
var body: some View {
ZStack {
Color.green
NavigationLink("View 3", destination: View3())
}
.navigationBarTitle("Visible Title 1")
.onAppear {
self.isNavigationBarHidden = false
}
}
}
struct View3: View {
var body: some View {
Color.blue
.navigationBarTitle("Visible Title 2")
}
}
Setting navigationBarHidden
to false
on views deeper in the navigation stack doesn't seem to properly override the preference of the view that originally set navigationBarHidden
to true
, so the only workaround I could come up with was using a binding to change the preference of the original view when a new view is pushed onto the navigation stack.
Like I said, this is a hacky solution, but without an official solution from Apple, this is the best that I've been able to come up with.
SwiftUI Navigation. View pops immediately after being pushed. How to fix?
You might be running into the bug where views pop immediately if there's exactly two navigation links. Try inserting a NavigationLink with EmptyViews as shown as a temporary bandaid.
NavigationLink(destination: EmptyView()) {
EmptyView()
}
You can find more info here:
https://forums.swift.org/t/14-5-beta3-navigationlink-unexpected-pop/45279
Issue with popping to a previous view (navigation controller)
First, it looks like your NavigationController is not your Initial View Controller. The arrow is pointing at the one in the middle, not the one on the left as I would expect.
As to your follow up question, removing the navigation bar at the top is easy enough.
Just add this to your UIViewControllers:
override func viewWillAppear(animated: Bool) {
self.navigationController?.navigationBar.hidden = true
}
As far as I know you have to remove it for each UIViewController in your stack.
One other thing. Your code for "back" can be simplified:
@IBAction func back(sender: AnyObject) {
self.navigationController?.popViewControllerAnimated(true)
}
In Swift 3:
@IBAction func gobackTapped(sender: AnyObject) {
_ = self.navigationController?.popViewController(animated: true)
}
The syntax is dumb, but this is how it is right now. I would assume at some point popViewController
will be marked as a @discardableResult
Related Topics
Projecting the Arkit Face Tracking 3D Mesh to 2D Image Coordinates
Reasons to Include Function in Protocol Definition VS. Only Defining It in the Extension
How to Create a Fixed-Size Array of Objects
How to Check If Time Is Within a Specific Range in Swift
Every Uialertcontroller Disappear Automatically Before User Responds - Since iOS 13
Perform Assignment Only If Right Side Is Not Nil
Avaudioengine Downsample Issue
Swift Custom Context Menu Previewprovider Can Not Click Any View Inside(Using Tapgesture)
Draw Scenekit Object Between Two Points
Scanning Real-World Object and Generating 3D Mesh from It
Get the Top Viewcontroller in iOS Swift
How to Use an Nsattributedstring with a Scrollview in Swiftui
How to Make Class Methods/Properties in Swift
Upvote/Downvote System Within Swift via Firebase
Hide/Show Tab Bar When Push/Back. Swift
Handling Multiple Gesturerecognizers