NavigationCoordinator acts as a coordinator for NavigationView in SwiftUI. You can use pushView, popView, popToView, popToRootView as you can in traditional UIKit

Overview

NavigationCoordinator

iOS: 13.0 Swift: 5.5 License: MIT Version: 0.0.1

NavigationCoordinator acts as a coordinator for NavigationView. You can use pushView, popView, popToView, popToRootView in SwiftUI as you can in traditional UIKit

Installing

Swift Package Manager

You can install it using SPM

https://github.com/SNNafi/NavigationCoordinator.git

Usage

Suppose, you want to programmatically push a view or pop a view or pop to any specific view or pop to root view. There is no way to do this in SwiftUI. NavigationCoordinator is the best in this situation. It does not include any extra or unnecessary dependency, at the same time, it works with UINavigationController as NavigationView does. Better say, it adds extra functionalities for NavigationView with the help of battle tested UINavigationController.

It supports working with multiple UINavigationController. Suppose, when you will present a modal, you will need another UINavigationController for this to manage navigation between views that is independent from the view presenting it. So, you need two UINavigationController. And this package has build in supports for that. But there is a little work needed from your side. Let's get started

First create an extension in NavigationControllerId

extension NavigationControllerId {
    // for any other navigation controller, just declare a `NavigationControllerId` for this to uniquely identify.
    // There is already `primaryNavigationController` for default navigation controller
    static let sheetNavigationController = "SheetNavigationController"
}

Suppose you have 5 view, HomeView, ListView, DetailView, SettingView, AboutView, ContactView, PrivacyView. Create an extension for them to uniquely identify them, so that we can pop to any view directly !

extension ViewId {
    static let homeView = "homeView"
    static let listView = "listView"
    static let detailView = "detailView"
    static let settingView = "settingView"
    static let aboutView = "aboutView"
    static let contactView = "contactView"
    static let privacyView = "privacyView"
}

You are ready to go

import SwiftUI
import NavigationCoordinator

struct ContentView: View {
    
    @Environment(\.navigationCoordinator) var navigationCoordinator
    
    var body: some View {
        NavigationView { // wrap in NavigationView
            VStack {
                Text("Hello, NavigationCoordinator Example!")
                    .padding()
                
                Button {
                    // push to HomeView
                    navigationCoordinator.pushView(withViewId: .homeView) {
                        HomeView()
                    }
                } label: {
                    Text("HomeView")
                }.padding()
            }
            .onAppear {
              
            }
            .navigationCoordinator(id: .primaryNavigationController) // initiate default navigation controller, do not call this again unless you need another navigation controller for modal
        }
    }
}

Suppose now by pushing, now you are in PrivacyView. That is like HomeView -> SettingView -> AboutView -> ContactView -> PrivacyView

Now you want to directly return to SettingView

import SwiftUI

struct SheetSixthView: View {
    
    @Environment(\.navigationCoordinator) var navigationCoordinator
  
    var body: some View {
        VStack {
            Text(String.currentFileName())
                .padding()
                .onTapGesture {
                    navigationCoordinator.popToView(viewId: .settingView) // this will do the rest for you
                }
        }
    }
}

What if you want to directly return to HomeView

import SwiftUI

struct SheetSixthView: View {
    
    @Environment(\.navigationCoordinator) var navigationCoordinator
  
    var body: some View {
        VStack {
            Text(String.currentFileName())
                .padding()
                .onTapGesture {
                    navigationCoordinator.popToRootView() // you are good to go
                }
        }
    }
}

Or, you just want to return to the previous view ?

import SwiftUI

struct SheetSixthView: View {
    
    @Environment(\.navigationCoordinator) var navigationCoordinator
  
    var body: some View {
        VStack {
            Text(String.currentFileName())
                .padding()
                .onTapGesture {
                     navigationCoordinator.popView() // are you still here ?
                }
        }
    }
}

And you need state based navigation ? NavigationLink is still here to rescue. As said, this package just coordinates, doesn't take way any exisiting feature.

What about modal ? Suppose you need to use another navigation controller.

import SwiftUI
import NavigationCoordinator

struct ModalView: View {
    
    @Environment(\.navigationCoordinator) var navigationCoordinator
    
    var body: some View {
        NavigationView { // wrap in NavigationView
            VStack {
                Text("Hello, NavigationCoordinator Example!")
                    .padding()
                
                Button {
                    // push to SomeView
                    navigationCoordinator.pushView(withViewId: .someView) {
                        SomeView()
                    }
                } label: {
                    Text("SomeView")
                }.padding()
            }
             .navigationCoordinator(id: .sheetNavigationController) // initiate another navigation controller
        }
    }
}

Important: When presenting this using sheet, need to set id back to the .primaryNavigationController in onDismiss . Like that,

 .sheet(isPresented: $isShow, onDismiss: { NavigationCoordinator.currentNavigationControllerId = .primaryNavigationController }, content: {
            ModalView()
        })

And, the rest thing as you do in previous steps.

Inject NavigationCoordinator

By default you can programmatically push and pop only inside the NavigationView hierarchy (by accessing the NavigationCoordinator environment). But you can do it from outside also

But there are few things you need to keep in mind

No matter what, you always need to initiate a navigation controller by using this .navigationCoordinator(id:) in view hierarchy. But it must need to perform in a view which is wrapped into a NavigationView

Like,

 NavigationView {
     SomeView()
         .navigationCoordinator(id: .primaryNavigationController) // this is a must. Otherwise, cannot track down the underlying UINavigationController
 }

Then you can do,

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    static let navigationCoordinator = NavigationCoordinator()

    .....
    .....
    let mainView = MainView()
        .environment(\.navigationCoordinator, Self.navigationCoordinator) // this is not necessary. But if you need, you can set it to use from another non view type like view model or router


   // Use a UIHostingController as window root view controller.
      if let windowScene = scene as? UIWindowScene {
          let window = UIWindow(windowScene: windowScene)
          window.rootViewController = UIHostingController(rootView: mainView)
          self.window = window
          window.makeKeyAndVisible()
      }
    .....
    .....
}


struct ContentView: View {

    var body: some View {
        NavigationView {
            HomeView(router: DefaultRouter())
                .navigationCoordinator(id: .primaryNavigationController)
        }
    }
}

class DefaultRouter {
    
    func navigateToList() {
        SceneDelegate.navigationCoordinator.pushView(withViewId: .fourthView) {
            FourthView()
        }
    }

    func navigateToDetail() {
        SceneDelegate.navigationCoordinator.pushView(withViewId: .fifthView) {
            FifthView()
        }
    }
}

struct HomeView: View {

    let router: DefaultRouter

    var body: some View {
        VStack {
            Text("HomeView")
            Button("ListView") {
                router.navigateToList()
            }
            Button("DetailView") {
                router.navigateToDetail()
            }
        }
    }
}

Status

This is an active project. I will try to add new features.

You might also like...
📸 A wrapper for AVCaptureSession - The way easier to use the Camera.

Capturer A wrapper for AVCaptureSession - The way easier to use the Camera. Setting up let captureBody = CaptureBody( configuration: .init { $0.

Very easy to use image loader for swift
Very easy to use image loader for swift

Silo Silo is a extremely easy to use and very basic image loader for iOS built in Swift. if you use Silo in your project please let me know! Version 2

Gallery has a clearer flow based on albums and focuses on the use case of selecting video
Gallery has a clearer flow based on albums and focuses on the use case of selecting video

Description We all love image pickers, don't we? You may already know of ImagePicker, the all in one solution for capturing pictures and selecting ima

Use Swift to pack ambiguous image that display differently on Apple and Other devices

Aibmoe Use Swift to pack ambiguous image that display differently on Apple and O

Small color quantizer for bitmaps without any dependencies or use of frameworks

MicroColorQuantizer This package currently offers a very simple color quantizer

An example app showing how to use AVCaptureSession with Metal in Swift.
An example app showing how to use AVCaptureSession with Metal in Swift.

#iOSSwiftMetalCamera Click here to see video demo. This app is a basic example showing how to use Swift to setup an AVCaptureSession session to access

Use `PreselectedPhotoViewController` to handle limited photo access case in your app.
Use `PreselectedPhotoViewController` to handle limited photo access case in your app.

PreselectedPhotoUI Use PreselectedPhotoViewController to handle limited photo access case in your app. Overview Supports iOS 14.0 or later How to use?

EasierPath is a library to make UIBezierPath easier to use
EasierPath is a library to make UIBezierPath easier to use

EasierPath is a library to make UIBezierPath easier to use. More specifically, you can write more intuitive and concise code when you draw a straight line or a curve.

Zoomable - A container that allows you to zoom in and out of an image using only SwiftUI

Zoomable It is a container that allows you to zoom in and out of an image using

Releases(0.0.1)
Owner
Shahriar Nasim Nafi
Shahriar Nasim Nafi
Fun GridScrollView written in SwiftUI + Combine, bridging between UIKit

BSZoomGridScrollView BSZoomGridScrollView is a powerful, pure swift iOS UI framework that provides the awesome grid scrollview containing your image a

Jang Seoksoon 148 Dec 17, 2022
PrivateImage - DRM image view for UIKit and SwiftUI

SecretImage Image view with DRM protection for SwiftUI and UIKit. What? This vie

Pavel Serdziukou 27 Dec 17, 2022
The repository for a command line / build pipeline tool for generating colors from a human-readable text file that designers can also use.

ColorPaletteGenerator ColorPaletteGenerator is a tool that takes a human-readable input file describing a color palette, and generates the associated

horseshoe7 0 Dec 7, 2021
Gifu adds protocol-based, performance-aware animated GIF support to UIKit.

Gifu adds protocol-based, performance-aware animated GIF support to UIKit. (It's also a prefecture in Japan). Install Swift Package Manager Add the fo

Reda Lemeden 2.8k Jan 7, 2023
LoremPicsum - Simple UIKit based app for displaying grid of pictures

LoremPicsum - Simple UIKit based app for displaying grid of pictures

Paweł Dziennik 0 Jan 20, 2022
ThreeDCardView - Library that you can see images with a 3D card 🌌

?? ThreeDCardView Usage First you have to import 'ThreeDCardView' import 'ThreeDCardView' Create ThreeDCardView and set the frame let threeDCardView:T

Fomagran 4 Jul 9, 2022
Lightbox is a convenient and easy to use image viewer for your iOS app

Lightbox is a convenient and easy to use image viewer for your iOS app, packed with all the features you expect: Paginated image slideshow. V

HyperRedink 1.5k Dec 22, 2022
A smart and easy-to-use image masking and cutout SDK for mobile apps.

TinyCrayon SDK for iOS A smart and easy-to-use image masking and cutout SDK for mobile apps. TinyCrayon SDK provides tools for adding image cutout and

null 1.8k Dec 30, 2022
SwiftGen is a tool to automatically generate Swift code for resources of your projects (like images, localised strings, etc), to make them type-safe to use.

SwiftGen is a tool to automatically generate Swift code for resources of your projects (like images, localised strings, etc), to make them type-safe to use.

null 8.3k Jan 5, 2023