An alternative SwiftUI NavigationView implementing classic stack-based navigation giving also some more control on animations and programmatic navigation.

Overview

swiftui-navigation-stack

An alternative SwiftUI NavigationView implementing classic stack-based navigation giving also some more control on animations and programmatic navigation.

NavigationStack

Installation

Swift Package Manager

The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into the swift compiler: open xCode, click on File -> Swift Packages -> Add Package dependency... and use the repository URL (https://github.com/matteopuc/swiftui-navigation-stack.git) to download the package.

In xCode, when prompted for Version or branch, the suggestion is to use Branch: master.

Then in your View simply include import NavigationStack and follow usage examples below.

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate NavigationStack into your Xcode project using CocoaPods, specify it in your Podfile:

pod 'NavigationStack'

Then in your View simply include import NavigationStack and follow usage examples below.

Usage

In SwiftUI we have a couple of views to manage the navigation: NavigationView and NavigationLink. At the moment these views have some limitations:

  • we can't turn off the transition animations;
  • we can't customise the transition animations;
  • we can't navigate back either to root (i.e. the first app view), or to a specific view;
  • we can't push programmatically without using a view;

NavigationStackView is a view that mimics all the behaviours belonging to the standard NavigationView, but it adds the features listed here above. You have to wrap your view hierarchy inside a NavigationStackView:

import NavigationStack

struct RootView: View {
    var body: some View {
        NavigationStackView {
            MyHome()
        }
    }
}

Jan-07-2020 15-40-35

You can even customise transitions and animations in some different ways. The NavigationStackView will apply them to the hierarchy:

  • you could decide to go for no transition at all by creating the navigation stack this way NavigationStackView(transitionType: .none);
  • you could create the navigation stack with a custom transition:
import NavigationStack

struct RootView: View {
    var body: some View {
        NavigationStackView(transitionType: .custom(.scale)) {
            MyHome()
        }
    }
}

Jan-10-2020 15-31-40

  • NavigationStackView has a default easing for transitions. The easing can be customised during the initialisation
struct RootView: View {
    var body: some View {
        NavigationStackView(transitionType: .custom(.scale), easing: .spring(response: 0.5, dampingFraction: 0.25, blendDuration: 0.5)) {
            MyHome()
        }
    }
}

Important: The above is the recommended way to customise the easing function for your transitions. Please, note that you could even specify the easing this other way:

NavigationStackView(transitionType: .custom(AnyTransition.scale.animation(.spring(response: 0.5, dampingFraction: 0.25, blendDuration: 0.5))))

attaching the easing directly to the transition. Don't do this. SwiftUI has still some problems with implicit animations attached to transitions, so it may not work. For example, implicit animations attached to a .slide transition won't work.

Push

In order to navigate forward you have two options:

  • Using the PushView;
  • Programmatically push accessing the navigation stack directly;

PushView

The basic usage of PushView is:

PushView(destination: ChildView()) {
    Text("PUSH")
}

which creates a tappable view (in this case a simple Text) to navigate to a destination. There are other ways to trigger the navigation using the PushView:

struct MyHome: View {
    @State private var isActive = false
    
    var body: some View {
        VStack {
            PushView(destination: ChildView(), isActive: $isActive) {
                Text("PUSH")
            }
            
            Button(action: {
                self.isActive.toggle()
            }, label: {
                Text("Trigger push")
            })
        }
    }
}

this way you have a tappable view as before, but you can even exploit the isActive bool to trigger the navigation (also in this case the navigation is triggered through the PushView).

If you have several destinations and you want to avoid having a lot of @State booleans you can use this other method:

enum ViewDestinations {
    case noDestination
    case child1
    case child2
    case child3
}

struct MyHome: View {
    @ObservedObject var viewModel: ViewModel
    @State private var isSelected: ViewDestinations? = .noDestination

    var body: some View {
        VStack {
            PushView(destination: ChildView1(), tag: ViewDestinations.child1, selection: $isSelected) {
                Text("PUSH TO CHILD 1")
            }

            PushView(destination: ChildView2(), tag: ViewDestinations.child2, selection: $isSelected) {
                Text("PUSH TO CHILD 2")
            }

            PushView(destination: ChildView3(), tag: ViewDestinations.child3, selection: $isSelected) {
                Text("PUSH TO CHILD 3")
            }

            Button(action: {
                self.isSelected = self.viewModel.getDestination()
            }, label: {
                Text("Trigger push")
            })
        }
    }
}

Now you have three tappable views and the chance to trigger the navigation through a tag (the navigation is always triggered by the PushView).

Push programmatically:

Inside the NavigationStackView you have access to the navigation stack as an EnvironmentObject. If you need to trigger the navigation programmatically without relying on a PushView (i.e. without having a tappable view) you can do like this:

struct MyHome: View {
    @ObservedObject var viewModel: ViewModel
    @EnvironmentObject private var navigationStack: NavigationStack

    var body: some View {
        Button(action: {
            self.viewModel.performBackgroundActivities(withCallback: {
                DispatchQueue.main.async {
                    self.navigationStack.push(ChildView())
                }
            })
        }, label: {
            Text("START BG ACTIVITY")
        })
    }
}

Specifying an ID

It's not mandatory, but if you want to come back to a specific view at some point later you need to specify an ID for that view. Both PushView and programmatic push allow you to do that:

struct MyHome: View {
    private static let childID = "childID"
    @ObservedObject var viewModel: ViewModel
    @EnvironmentObject private var navigationStack: NavigationStack

    var body: some View {
        VStack {
            PushView(destination: ChildView(), destinationId: Self.childID) {
                Text("PUSH")
            }
            Button(action: {
                self.viewModel.performBackgroundActivities(withCallback: {
                    DispatchQueue.main.async {
                        self.navigationStack.push(ChildView(), withId: Self.childID)
                    }
                })
            }, label: {
                Text("START BG ACTIVITY")
            })
        }
    }
}

Pop

Pop operation works as the push operation. We have the same two options:

  • Using the PopView;
  • Programmatically pop accessing the navigation stack directly;

PopView

The basic usage of PopView is:

struct ChildView: View {
    var body: some View {
        PopView {
            Text("POP")
        }        
    }
}

which pops to the previous view. You can even specify a destination for your pop operation:

struct ChildView: View {
    var body: some View {
        VStack {
            PopView(destination: .root) {
                Text("POP TO ROOT")
            }
            PopView(destination: .view(withId: "aViewId")) {
                Text("POP TO THE SPECIFIED VIEW")
            }
            PopView {
                Text("POP")
            }
        }
    }
}

PopView has the same features as the PushView. You can create a PopView that triggers with the isActive bool or with the tag. Also, you can trigger the navigation programmatically without relying on the PopView itself, but accessing the navigation stack directly:

struct ChildView: View {
    @ObservedObject var viewModel: ViewModel
    @EnvironmentObject private var navigationStack: NavigationStack

    var body: some View {
        Button(action: {
            self.viewModel.performBackgroundActivities(withCallback: {
                self.navigationStack.pop()
            })
        }, label: {
            Text("START BG ACTIVITY")
        })
    }
}

NavigationStack injection

By default you can programmatically push and pop only inside the NavigationStackView hierarchy (by accessing the NavigationStack environment object). If you want to use the NavigationStack outside the NavigationStackView you need to create your own NavigationStack (wherever you want) and pass it as a parameter to the NavigationStackView. This is useful when you want to decouple your routing logic from views.

Important: Every NavigationStack must be associated to a NavigationStackView. A NavigationStack cannot be shared between multiple NavigationStackView.

For example:

struct RootView: View {
    let navigationStack: NavigationStack

    var body: some View {
        NavigationStackView(navigationStack: navigationStack) {
            HomeScreen(router: MyRouter(navStack: navigationStack))
        }
    }
}

class MyRouter {
    private let navStack: NavigationStack

    init(navStack: NavigationStack) {
        self.navStack = navStack
    }

    func toLogin() {
        self.navStack.push(LoginScreen())
    }

    func toSignUp() {
        self.navStack.push(SignUpScreen())
    }
}

struct HomeScreen: View {
    let router: MyRouter

    var body: some View {
        VStack {
            Text("Home")
            Button("To Login") {
                router.toLogin()
            }
            Button("To SignUp") {
                router.toSignUp()
            }
        }
    }
}

Important

Please, note that NavigationStackView navigates between views and two views may be smaller than the entire screen. In that case the transition animation won't involve the whole screen, but just the two views. Let's make an example:

struct Root: View {
    var body: some View {
        NavigationStackView {
            A()
        }
    }
}

struct A: View {
    var body: some View {
        VStack(spacing: 50) {
            Text("Hello World")
            PushView(destination: B()) {
                Text("PUSH")
            }
        }
        .background(Color.green)
    }
}

struct B: View {
    var body: some View {
        PopView {
            Text("POP")
        }
        .background(Color.yellow)
    }
}

The result is:

Jan-10-2020 15-47-43

The transition animation uses just the minimum amount of space necessary for the views to enter/exit the screen (i.e. in this case the maximum width between view1 and view2) and this is exactly how it is meant to be.

On the other hand you also probably want to use the NavgationStackView to navigate screens. Since in SwiftUI a screen (the old UIKit ViewController) it's just a View I suggest you create an handy and simple custom view called Screen like this:

extension Color {
    static let myAppBgColor = Color.white
}

struct Screen<Content>: View where Content: View {
    let content: () -> Content

    var body: some View {
        ZStack {
            Color.myAppBgColor.edgesIgnoringSafeArea(.all)
            content()
        }
    }
}

Now we can rewrite the example above using the Screen view:

struct Root: View {
    var body: some View {
        NavigationStackView {
            A()
        }
    }
}

struct A: View {
    var body: some View {
        Screen {
            VStack(spacing: 50) {
                Text("Hello World")
                PushView(destination: B()) {
                    Text("PUSH")
                }
            }
            .background(Color.green)
        }
    }
}

struct B: View {
    var body: some View {
        Screen {
            PopView {
                Text("POP")
            }
            .background(Color.yellow)
        }
    }
}

This time the transition animation involves the whole screen:

Jan-10-2020 16-10-59

Issues

  • SwiftUI resets all the properties of a view marked with @State every time the view is removed from a view hierarchy. For the NavigationStackView this is a problem because when I come back to a previous view (with a pop operation) I want all my view controls to be as I left them before (for example I want my TextFields to contain the text I previously typed in). In order to workaround this problem you have to use @ObservableObject when you need to make some state persist between push/pop operations. For example:
class ViewModel: ObservableObject {
    @Published var text = ""
}

struct MyView: View {
    @ObservedObject var viewModel: ViewModel
    
    var body: some View {
        VStack {
            TextField("Type something...", text: $viewModel.text)
            PushView(destination: MyView2()) {
                Text("PUSH")
            }
        }
    }
}

Other

SwiftUI is really new, there are some unexpected behaviours and several API not yet documented. Please, report any issue may arise and feel free to suggest any improvement or changing to this implementation of a navigation stack.

Comments
  • iOS 16 transitions malfunctioning

    iOS 16 transitions malfunctioning

    Hi there, After updating a device to iOS 16 the navigation is presenting some strange malfunctioning sometimes: Here is a View being pushed in the old version, entering from the right and "pushing" the old one to the left: old

    Here is same views with iOS 16 (never happened before). Now the old view starts to go to the right leaving a black space behind, at the same time that the new one is coming also from the right, making a strange animation: new

    It also happens (but the other way around) when poping views.

    Thanks a lot!!

    opened by enricgd 9
  • NavigationStack stops functioning once I open a fullScreenCover

    NavigationStack stops functioning once I open a fullScreenCover

    I have this chat tab on my app which uses a fullScreenCover. The problem is, after I open that fullScreenCover, all the Push functions of the navigationStack stop working. They animate but done push to anything, its like it is sliding itself on top of it.

    opened by ch05enOne 9
  • Xcode 14 Compiling Issue

    Xcode 14 Compiling Issue

    Hello, after updating to Xcode 14, I am not able to run my project because name NavigationStack conflicts with one of the new SwiftUI Native classes.

    Has anyone else ran into this issue? If so, were you able to find a solution?

    opened by BerkinSi 6
  • Mandatory pops to the root view when modifying observed object in the stacked view

    Mandatory pops to the root view when modifying observed object in the stacked view

    Hi, this one of the great library I ever found. However when trying to modifying an observed object in the second view (stack view), then it pop to the root view immediately, here is the minimum code to reproduce the issue, please have a look:

    import SwiftUI
    import NavigationStack
    
    class Model : ObservableObject {
        @Published var counter1 : Int = 0
        @Published var counter2 : Int = 0
    }
    
    struct SecondView: View {
        @ObservedObject var model : Model
        
        var body: some View {
            VStack() {
                Text("2st Page")
                Button(action: {
                    model.counter2 += 1
                    print(model.counter2)
                }, label: {
                    Text("Press to increase (\(model.counter2))")
                })
                PopView(label: {
                    Text("Back")
                })
            }
        }
    }
    
    struct ContentView: View {
        @ObservedObject var model : Model = Model()
        
        var body: some View {
            NavigationStackView() {
                VStack() {
                    Text("1st Page")
                    Button(action: {
                        model.counter1 += 1
                        print(model.counter1)
                    }, label: {
                        Text("Press to increase (\(model.counter1))")
                    })
                    PushView(destination: SecondView(model : model), label: {
                        Text("Go to 2nd view")
                    })
                }
            }
        }
    }
    

    When press the "Press to increase" button in the second view, the app pop to the root view immediately Runs on Xcode 12.2 & iOS simulator iPhone 12 Pro, iOS 14.2

    opened by xarple 6
  • Fatal error: No observable object of type NavigationStack.Type found (iOS 13.0)

    Fatal error: No observable object of type NavigationStack.Type found (iOS 13.0)

    Hello there!

    First of all, thank you for this amazing library which fixed many problems we had while implementing a navigation stack on our own, e.g. when using conditional navigation via switch-case-statements.

    While this library is working completely fine on iOS 13.1, 13.2.2 and 13.3, it leads to the following crash during app start on iOS 13.0:

    Fatal error: No observable object of type NavigationStack.Type found. A View.environmentObject(_:) for NavigationStack.Type may be missing as an ancestor of this view.: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-24.4/Core/EnvironmentObject.swift, line 161 Fatal error: No observable object of type NavigationStack.Type found. A View.environmentObject(_:) for NavigationStack.Type may be missing as an ancestor of this view.: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-24.4/Core/EnvironmentObject.swift, line 161 (lldb)

    There seems to be a problem with attaching NavigationStack as an EnvironmentObject. We were able to fix this issue by setting it as an EnvironmentObject by our own. This though is not possible with the library as it is, since its initializes is internal and therefor not accessible.

    My question is now how to fix this issue on iOS 13.0. If there is no solution, maybe you could provide a public initializer as a workaround, so we can add the EnvironmentObject by our own.

    Thanks in advance and best regards, Philipp

    opened by Faltenreich 5
  • How to manually make view .root

    How to manually make view .root

    I have this app where I use userDefaults to store if the user was previously logged in. If they weren't previousy logged in, the first view is a login page. If they were previously logged in, the first view is a different view. That said, I want to implement a logout feature. But if they were already logged in, and the first view is not the login page, how can I pop back to a view that never appeared ie (the login page).

    opened by ch05en 4
  • No ObservableObject of type NavigationStack found. A View.environmentObject(_:) for NavigationStack may be missing as an ancestor of this view.

    No ObservableObject of type NavigationStack found. A View.environmentObject(_:) for NavigationStack may be missing as an ancestor of this view.

    Fantastic library.

    What I did:

    • I created a NavigationViewModel that will handle navigation across pages that need it. See NavigationVM below.
    • Programmatically want to push to the next view while providing the viewId

    Error: No ObservableObject of type NavigationStack found. A View.environmentObject(_:) for NavigationStack may be missing as an ancestor of this view.

    Thank you for your help.

    import Foundation
    import SwiftUI
    import NavigationStack
    
    class NavigationVM: ObservableObject {
        @EnvironmentObject private var navigationStack: NavigationStack
        
        init(){
            
        }
        
        func pushView(view: AnyView, viewId: String? = nil){
            DispatchQueue.main.async {
                if viewId?.isEmpty == true {
                    self.navigationStack.push(view)
                } else {
                    self.navigationStack.push(view, withId: viewId!)
                }
            }
        }
        
        func popView(viewId: String? = nil){
            DispatchQueue.main.async {
                if viewId?.isEmpty == true {
                    self.navigationStack.pop()
                } else {
                    self.navigationStack.pop(to: PopDestination.view(withId: viewId!))
                }
            }
        }
        
        func popToRoot() {
            DispatchQueue.main.async {
                self.navigationStack.pop(to: .root)
            }
        }
    }
    

    Here's my implementation:

    var body: some View {
            NavigationStackView {
                  merchantGroupsBody()
            }
    }
    
    private func merchantGroupsBody() -> some View {
            VStack{
                ForEach(exploreVM.merchantGroups) { merchantGroup in
                    if merchantGroup.merchants.count > 0 {
                        VStack (alignment: .leading){
                            merchantGroupHeaderBody(merchantGroup)
                            ScrollView(.horizontal){
                                HStack{
                                    ForEach(merchantGroup.merchants, id: \.self){ merchant in
                                        merchantBody(merchant)
                                    }
                                }
                            }
                        }
                        //.foregroundColor(.white)
                    }
                }
            }
        }
    
    private func merchantBody(_ merchant: Merchant) -> some View {
            var alreadyCached: Bool {
                ImageCache.default.isCached(forKey: merchant.locationImageUrl)
            }
            
            return
                //NavigationLink(destination: MerchantView(MerchantVM(merchant))) {
                VStack (alignment: .leading) {
                    KFImage(URL(string: merchant.attachments.first!.url))
                        .onSuccess { r in
                            print("Success: \(merchant.name) - \(r.cacheType)")
                        }
                        .onFailure { e in
                            print("Error for mechant: \(merchant.name): \(e)")
                        }
                        .onProgress { downloaded, total in
                            print("\(downloaded) / \(total))")
                        }
                        .placeholder {
                            HStack {
                                Image(systemName: "arrow.2.circlepath.circle")
                                    .resizable()
                                    .frame(width: 50, height: 50)
                                    .padding(10)
                                Text("Loading...").font(.title)
                            }
                            .foregroundColor(.gray)
                        }
                        .cancelOnDisappear(true)
                        .resizable()
                        .frame(width: 200, height: 100)
                        .aspectRatio(contentMode: .fill)
                        .opacity(doneLoadingImage || alreadyCached ? 1.0 : 0.3)
                        .animation(.linear(duration: 0.4))
                    Text(merchant.name)
                    Text("\(merchant.distanceToCustomerString) | \(merchant.hoursOfOperationString)")
                        .font(.system(size:12))
                    Spacer()
                }
                .onTapGesture {
                    navigationVM.pushView(view: AnyView(MerchantView(MerchantVM(merchant))), viewId: CustomerViewIds.MerchantView.rawValue)
                }
            //}
        }
    
    opened by optimalpursuits 4
  • onAppear called immediately after push/pop

    onAppear called immediately after push/pop

    Hi there,

    First, thanks for this stack, really useful for my small side project as a SwiftUI rookie.

    Actually, I've just encountered the following behavior: onAppear is called immediately when pushing/popping pages, and not when the transition is finished. As I have an advanced custom transition taking more time and leading to a page that will have its own animation, I would like onAppear to be delayed until the transition is finished.

    I believe this is not really a bug but I'm wondering if this could be solved by this stack. Note: I can probably use state and enhance my custom transition to workaround (even though tbh custom transitions are a bit buggy when I want to deal with timing/delays).

    Here is a simple example:

    struct DestinationView: View {
        var body: some View {
            VStack {
                Spacer()
                HStack {
                    PopView {
                        Text("Page 2").onAppear {
                            print("Page 2 onAppear") // Printed immediately
                        }
                    }
                }
                Spacer()
            }
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
        }
    }
    
    struct Transition_Previews: PreviewProvider {
        static var previews: some View {
            VStack {
                NavigationStackView(transitionType: .default,
                                    easing: Animation.linear(duration: 5)) {
                                        PushView(destination: DestinationView()) {
                                            HStack {
                                                Spacer()
                                                Text("Page 1").onAppear {
                                                    print("Page 1 onAppear") // Printed immediately
                                                }
                                                Spacer()
                                            }
                                        }
                }
            }
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
        }
    }
    
    opened by spifd 4
  • popping a view makes the previous view non clickable

    popping a view makes the previous view non clickable

    I have a home screen with 3 Tabs one of the tab has scroll view with some clickable items, on pushing this navigation view stack, and popping back the view remains non-clickable unless the scroll is moved a little and then everything is back to clickable again.

    HELP!

    opened by divyanshunegi 4
  • @State variables get reset after pop

    @State variables get reset after pop

    Using a standard NavigationView and NavigationLink, the @State variables retain their values when pushed and subsequently popped back into view. iOS example:

    import SwiftUI
    
    
    struct ContentView: View {
        var body: some View {
            NavigationView {
                AAA()
            }
        }
    }
    
    struct AAA: View {
        @State var text = "default text"
        var body: some View {
            VStack {
                TextEditor(text: $text)
                NavigationLink(destination: BBB()) {
                    Text("Push")
                }
            }
        }
    }
    
    struct BBB: View {
        var body: some View {
            Text("BBB")
        }
    }
    
    

    Edit the text, click Push, then go Back. The texteditor retains the changes that were made before pushing.

    I tried the same thing with StackNavigationView and find that my @State variable is reset after a pop.

    iOS example:

    import SwiftUI
    import NavigationStack
    
    struct ContentView: View {
        var body: some View {
            NavigationStackView {
                AAA().padding()
            }
        }
    }
    
    struct AAA : View {
        @State private var data = "default text"
    
        var body: some View {
            VStack {
                TextEditor(text: $data)
                PushView(destination: BBB()) {
                    Text("Push")
                }
            }
        }
    }
    
    struct BBB : View {
        var body: some View {
            PopView {
                Text("Pop")
            }
        }
    }
    

    If you edit text the editor, hit Push, then Pop back you see the text has been reset to its default state.

    Is this expected behavior?

    opened by rjo 3
  • Navigation Stack doesn't work with WatchOS

    Navigation Stack doesn't work with WatchOS

    I've recently added a WatchOS target to an existing project that I developed with the Navigation Stack framework, and the WatchOS portion of the build fails if I try to "import NavigationStack". I've built a simple stand-alone app that demonstrates the problem in a really simple project: https://github.com/pkrakow/watchOS-Framework-Test

    opened by pkrakow 3
  • Callback navigation sometimes doesnt work

    Callback navigation sometimes doesnt work

    Hi I have implemented this library and it works great for my app for the most part. I have implemented a registration flow were the user goes through certain steps and moves to the next page. I have noticed that for when creating an account first time everything works as it should. But if i log out or kill the app and try to create a second account on a callback function i hit the self.navigation.push okay but the screen does not move the user forward. Here is the code below:

                            Button(action: {
                                viewModel.isLoading = true
                                viewModel.signUp(firstName: viewModel.firstName, lastName: viewModel.lastName, onCompletion: { result in
                                    switch result {
                                    case true:
                                        print("Go to verify page")
                                        if viewModel.showVerifyEmail {
                                            DispatchQueue.main.async {
                                                self.navigationStack.push(VerifyEmailView(authHandler: authHandler, apiHandler: apiHandler, verifying: true, emailAddress: viewModel.email.lowercased(), firstName: viewModel.firstName, lastName: viewModel.lastName, userExists: viewModel.userExists))
                                            }
                                        }
                                    case false:
                                        if viewModel.goBack {
                                            DispatchQueue.main.async {
                                                self.navigationStack.pop()
                                            }
                                        }
                                    }
                                })
                            }) {
                                PrimaryButtonMed(btnTitle: "create_account", isLoading: $viewModel.isLoading)
                            }
                            .opacity(viewModel.enableButton(firstName: viewModel.firstName, lastName: viewModel.lastName) ? 1 : 0.6)
                            .disabled(!viewModel.enableButton(firstName: viewModel.firstName, lastName: viewModel.lastName))
                        }
                        .padding(.trailing, 10)
    

    Any help of suggestions is greatly appreciated.

    Thanks

    opened by cmason9 0
  • Return to the same position in the previous view

    Return to the same position in the previous view

    Hello,

    Every time I want to go back to the main view (which is a list), I go back to the top of the view instead of going back to the item where I was in the list (normal behavior of iOS).

    Thank you

    opened by Djibs 2
  • Push doesn't work if app is backgrounded and aware of scenePhase

    Push doesn't work if app is backgrounded and aware of scenePhase

    Hello 👋

    I discovered a weird bug that nativation was not working after user,

    • open the app
    • background it
    • open again
    • then non of the progromatic push in entire project is not working
    struct SliderView: View {
        @EnvironmentObject private var navigationStack: NavigationStack
        
        var body: some View {
        ...
    			navigationStack.push(SomeView()) // not working
        ...
        }   
    }
    

    and I fix the bug by removing this @Environment(\.scenePhase) var scenePhase where NavigationStackView is located

    struct MainView: View {
    
        @StateObject var observable: MainObservable = koin.get()
    
        @Environment(\.scenePhase) var scenePhase // this line is removed and bug is fixed
    
        var body: some View {
    
            NavigationStackView(
                transitionType: .default,
                easing: Animation.easeInOut(duration: 0.5)
            ) {
    				....
            }
        }
    }
    

    I do not need any more scenePhase so it is not a blocker for me, but i thought it will be good to make you aware of this issue

    opened by mustafaozhan 0
  • Stack Reset / Jump to a completely different view

    Stack Reset / Jump to a completely different view

    Thanks for this project! I'm interested in the following functionality: Consider, we have a list of views in the stack: [Z, A, B, C] I'd like to reset stack completely to have new view hierarchy as [Z, X] by e.g. pressing a button in the view C. Is this possible as of right now?

    opened by richardtop 0
  • Full Screen cover equivalent

    Full Screen cover equivalent

    Is there an equivalent to a SwiftUI .fullScreenCover( )? https://www.hackingwithswift.com/quick-start/swiftui/how-to-present-a-full-screen-modal-view-using-fullscreencover

    opened by alelordelo 2
Owner
Matteo
Passionate iOS developer for almost six years. SwiftUI/Combine early adopter.
Matteo
Custom navigation swiftui NavigationLink NavigationView

Custom navigation swiftui Experimenting with navigation link. if you find this idea interesting you can take and expend it into a more powerful soluti

Igor 5 Dec 2, 2022
A lightweight iOS mini framework that enables programmatic navigation with SwiftUI, by using UIKit under the hood.

RouteLinkKit A lightweight iOS mini framework that enables programmatic navigation with SwiftUI. RouteLinkKit is fully compatible with native Navigati

Αθανάσιος Κεφαλάς 4 Feb 8, 2022
Tools for making SwiftUI navigation simpler, more ergonomic and more precise.

SwiftUI Navigation Tools for making SwiftUI navigation simpler, more ergonomic and more precise. Motivation Tools Navigation overloads Navigation view

Point-Free 1.1k Jan 1, 2023
NavigationViewKit is a NavigationView extension library for SwiftUI.

NavigationViewKit 中文版说明 NavigationViewKit is a NavigationView extension library for SwiftUI. For more detailed documentation and demo, please visit 用N

东坡肘子 74 Dec 28, 2022
A drop-in universal library helps you to manage the navigation bar styles and makes transition animations smooth between different navigation bar styles

A drop-in universal library helps you to manage the navigation bar styles and makes transition animations smooth between different navigation bar styles while pushing or popping a view controller for all orientations. And you don't need to write any line of code for it, it all happens automatically.

Zhouqi Mo 3.3k Dec 21, 2022
FlowStacks allows you to hoist SwiftUI navigation and presentation state into a Coordinator

FlowStacks allow you to manage complex SwiftUI navigation and presentation flows with a single piece of state. This makes it easy to hoist that state into a high-level coordinator view. Using this pattern, you can write isolated views that have zero knowledge of their context within the navigation flow of an app.

John Patrick Morgan 471 Jan 3, 2023
SwiftUINavigator: a lightweight, flexible, and super easy library which makes SwiftUI navigation a trivial task

The logo is contributed with ❤️ by Mahmoud Hussein SwiftUINavigator is a lightwe

OpenBytes 22 Dec 21, 2022
sRouting - The lightweight navigation framework for SwiftUI.

sRouting The lightweight navigation framework for SwiftUI. Overview sRouting using the native navigation mechanism in SwiftUI. It's easy to handle nav

Shiro 8 Aug 15, 2022
Change SwiftUI Navigation Bar Color for different View

SwiftUINavigationBarColor Change SwiftUI NavigationBar background color per screen. Usage For NavigationBarColor to work, you have to set the Navigati

Hai Feng Kao 18 Jul 15, 2022
🧭 SwiftUI navigation done right

?? NavigationKit NavigationKit is a lightweight library which makes SwiftUI navigation super easy to use. ?? Installation ?? Swift Package Manager Usi

Alex Nagy 152 Dec 27, 2022
Navigation helpers for SwiftUI applications build with ComposableArchitecture

Swift Composable Presentation ?? Description Navigation helpers for SwiftUI applications build with ComposableArchitecture. More info about the concep

Dariusz Rybicki 52 Dec 14, 2022
Make SwiftUI Navigation be easy

VNavigator VNavigator is a clean and easy-to-use navigation in SwiftUI base on UINavigationController in UIKit Installation From CocoaPods CocoaPods i

Vu Vuong 10 Dec 6, 2022
SwiftUINavigation provides UIKit-like navigation in SwiftUI

SwiftUINavigation About SwiftUINavigation provides UIKit-like navigation in Swif

Bhimsen Padalkar 1 Mar 28, 2022
Simple iOS app to showcase navigation with coordinators in SwiftUI + MVVM.

SimpleNavigation Simple iOS app to showcase the use of the Coordinator pattern using SwiftUI and MVVM. The implementation is as easy as calling a push

Erik Lopez 7 Dec 6, 2022
Backported SwiftUI navigation APIs introduced in WWDC22

Navigation Backport This package uses the navigation APIs available in older SwiftUI versions (such as NavigationView and NavigationLink) to recreate

John Patrick Morgan 532 Dec 29, 2022
A view that shows selectable symbols, similar to UITableView's `sectionIndexTitles` API but with support for symbols and more flexibility

?? TableOfContentsSelector Are you familiar with UITableView's sectionIndexTitles API? The little alphabet on the side of some tables for quickly jump

Christian Selig 106 Dec 19, 2022
Easily hide and show a view controller's navigation bar (and tab bar) as a user scrolls

HidingNavigationBar An easy to use library (written in Swift) that manages hiding and showing a navigation bar as a user scrolls. Features Usage Custo

Tristan Himmelman 1k Dec 21, 2022
Simple and integrated way to customize navigation bar experience on iOS app.

NavKit Simple and integrated way to customize navigation bar experience on iOS app. It should save our time that we usually use to make abstraction of

Wilbert Liu 37 Dec 7, 2022
Models UI navigation patterns using TCA

Composable Navigation The Composable Navigation is a Swift Package that builds on top of The Composable Architecture (TCA, for short). It models UI na

Michael Heinzl 41 Dec 14, 2022