SwiftUINavigator: a lightweight, flexible, and super easy library which makes SwiftUI navigation a trivial task

Overview

SwiftUINavigator Logo

The logo is contributed with ❤️ by Mahmoud Hussein

platform iOS swift v5.3 deployment target iOS 13

SwiftUINavigator is a lightweight, flexible, and super easy library which makes SwiftUI navigation a trivial task.

Table of contents

Why?

Let's first explore the limitation of SwiftUI then explore the awesome features SwiftUINavigator provides.

SwiftUI Limitations

In SwiftUI, there are a lot of limitations:

  • Transition navigations can not be disabled or customized.
  • Can not ignore adding the view to the back stack.
  • No navigation back to root view.
  • Can not navigate to a view using a specific ID.
  • Inconsistent navigation when use NavigationLinka, .sheet and .fullScreenCover
  • Can not navigate programmatically.
  • Customizing the navigation bar is not trivial.

SwiftUINavigator is awesome

SwiftUINavigator has a lot of awesome features. Here's some of these features:

  • Custom navigation transitions
  • Navigate to a view without adding it to the back stack.
  • Direct navigation without links
  • Direct navigation with links
  • Present sheets without having to declare a sheet modifier.
  • Dismiss to previous view.
  • Dismiss to root view.
  • Dismiss to a specific view using its ID.
  • Navigation Bars are built-in the library

Requirements

  • iOS 13+
  • Swift 5.3+

Installation

📦 Swift Package Manager

https://github.com/Open-Bytes/SwiftUINavigator.git

Usage

  1. Import SwiftUINavigator.
import SwiftUINavigator
  1. Declare NavigatorView in the root view of the app.
NavigatorView {
    HomeScreen()
}

NavigatorView supports transition animations and other options. See NavigatorView

  1. Navigate to your destination view:
  • Using NavigatorLink.
NavigatorLink(destination: SomeView()) {
    // When this view is clicked, it will trigger 
    // the navigation and show the destination view
    ProductItemView()
}

For more details aboutNavigator, see NavigatorLink

  • Or using Navigator
@EnvironmentObject private var navigator: Navigator

navigator.navigate(SomeView())

For more details aboutNavigator, see Navigator

  1. Dismiss (navigate back) to the previous view programmatically (using Navigator) or using a link (using DismissLink).
  • Using NavigatorLink.
DismissLink {
    Label("Back", systemImage: "chevron.backward")
            .foregroundColor(.blue)
}
  • Or using Navigator
navigator.dismiss()

For more details about dismissing, see Dismissing (Navigation Back)

NavigatorView

NavigatorView is the alternative of SwiftUI NavigationView implementing stack-based navigation with mote control and flexibility in handling more the navigation login

The public initializers

public init(
        transition: NavigatorTransitionType = .default,
        easeAnimation: Animation = .easeOut(duration: 0.2),
        @ViewBuilder rootView: () -> Root)

public init(
        navigator: Navigator,
        transition: NavigatorTransitionType = .default,
        easeAnimation: Animation = .easeOut(duration: 0.2),
        @ViewBuilder rootView: () -> Root)

As you can see, you can customize the transition animation and easeAnimation.

NavigatorView(
        transition: .custom(push: .scale, pop: .slide),
        easeAnimation: .easeInOut) {
    HomeScreen()
}

Important Note: the second initializers supports a Navigator instance. This is important if you need to nest a NavigatorView other than the root one. Keep in mind that if you didn't pass the Navigator instance, it will work, but it's recommended to pass it for consistent behavior is the whole app. In this case, you should pass the instance of Navigator using the EnvironmentObject as follows:

@EnvironmentObject private var navigator: Navigator

NavigatorView(navigator: navigator) {
    SomeView()
}

For more details about NavigatorTransitionType, see Navigation Transition Types

Navigator

The Navigator class is the heart of the library. It's injected to any view as EnvironmentObject.

@EnvironmentObject private var navigator: Navigator

You can use Navigator directly to navigate programmatically to any view with 3 options

  1. Push view (Regular Navigation)
navigator.navigate(ProductDetailScreen(item: item))
// OR
navigator.navigate(ProductDetailScreen(item: item), type: .push())

You can specify an ID for the pushed view navigate(SomeView(), type: .push(id: "Detail Screen")). Later, you can use this ID to navigate back to the view it's belonging to. See Dismissing (Navigation Back)

You can ignore adding the view to tha back stack navigate(SomeView(), type: .push(addToBackStack: false)). When you navigate back this view won't be displayed. See Dismissing (Navigation Back)

  1. Present sheet
navigator.navigate(ProductDetailScreen(item: item), type: .sheet)
  1. Present full sheet
navigator.navigate(ProductDetailScreen(item: item), type: .fullSheet)

The navigation types are declared in NavigationType enum. See Navigation Types

NavigatorLink

The alternative of NavigationLink. It's a wrapper of Navigator. When clicked, it will navigate to the destination view with the specified navigation type.

NavigatorLink(destination: ProductDetailScreen(item: item)) {
    // When this view is clicked, it will trigger 
    // the navigation and show the destination view
    ProductItemView(item: item)
}

Dismissing (Navigation Back)

You can dismiss the current view:

  • Using NavigatorLink.
DismissLink {
    Label("Back", systemImage: "chevron.backward")
            .foregroundColor(.blue)
}
  • Or using Navigator
navigator.dismiss()

Important Note: You have 4 options in dismissing the current view. for more details, see DismissDestination

DismissDestination

DismissDestination Defines the type of dismiss operation.

public enum DismissDestination {
    /// Navigate back to the previous view.
    case previous

    /// Navigate back to the root view (i.e. the first view added
    /// to the NavigatorView during the initialization process).
    case root

    /// Navigate back to a view identified by a specific ID.
    case view(withId: String)

    // Dismiss current presented sheet
    case dismissSheet
}

You can pass your option to DismissLink or Navigator.dismiss()

DismissLink(to: .root) {
    Label("Back", systemImage: "chevron.backward")
            .foregroundColor(.blue)
}

navigator.dismiss(to: .root)

Navigation Bar

Sine we don't use SwiftUI's NavgationView, the default navigation bar won't be displayed. To show the navigation bar you can use the library built-in bars or customize one.

SomeView()
        .navBar(
                style: .normal,
                leadingView: {
                    SomeView()
                }
        )

NavBarStyle supports normal and `large navigation bars.

Transitions

NavigatorView(transition: .custom(push: .scale, pop: .slide)) {
    SomeView()
}

Navigation Types

This enum defines the supported navigation types

public enum NavigationType {
    /// Defines the regular navigation type.
    /// id: pass a custom ID to use when navigate back.
    /// addToBackStack: if false, the view won't be added to the back stack 
    /// and won't be displayed when dismissing the view.
    case push(id: String? = nil, addToBackStack: Bool = true)
    /// Present a sheet
    case sheet
    /// Present a full sheet
    @available(iOS 14.0, *)
    case fullSheet
}

Navigation Transition Types

NavigatorTransitionType enum defines the supported transition types.

public enum NavigatorTransitionType {
    /// Transitions won't be animated.
    case none

    /// The default transition if you didn't pass one.
    case `default`

    /// Use a custom transition.
    case custom(push: AnyTransition, pop: AnyTransition)
}

👏 Contribution

All Pull Requests (PRs) are welcome. Help us make this library better.

License

click to reveal License
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
You might also like...
SwiftUINavigation provides UIKit-like navigation in SwiftUI

SwiftUINavigation About SwiftUINavigation provides UIKit-like navigation in Swif

Simple iOS app to showcase navigation with coordinators in SwiftUI + MVVM.
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

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

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

Easily hide and show a view controller's navigation bar (and tab bar) as a user scrolls
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

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

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

An iOS view-controller navigation management. No inherit, using one line code to integrate.
An iOS view-controller navigation management. No inherit, using one line code to integrate.

KGNavigationBar Example An iOS view-controller navigation management. No inherit, using one line code to integrate. 一个 iOS 控制器导航管理库. 无需继承, 一行代码即可实现集成。

Powerful navigation in the Composable Architecture via the coordinator pattern

TCACoordinators The coordinator pattern in the Composable Architecture TCACoordinators brings a flexible approach to navigation in SwiftUI using the C

Comments
  • 0.3.0 fails to compile on macOS

    0.3.0 fails to compile on macOS

    Your project doesn't integrate into a macOS project. See errors below.

    I would have loved to use this package, but will likely need to find another solution now.

    Showing Recent Messages
    CompileSwift normal arm64 /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavBar/NavBar.swift (in target 'SwiftUINavigator' from project 'SwiftUINavigator')
        cd /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator
        /users/redacted/Downloads/Xcode\ 13.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend -frontend -c /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/BackStack.swift /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/BottomSheet/BottomSheet.swift /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/DismissLink.swift /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/Extensions/View+OnDataChange.swift /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/LazyView.swift /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavBar/LargeNavBar.swift -primary-file /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavBar/NavBar.swift /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavManager.swift /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavigationType.swift /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/Navigator.swift /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavigatorLink.swift /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavigatorTransitionType.swift /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavigatorView.swift -emit-dependencies-path /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/Build/Intermediates.noindex/SwiftUINavigator.build/Debug/SwiftUINavigator.build/Objects-normal/arm64/NavBar.d -emit-reference-dependencies-path /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/Build/Intermediates.noindex/SwiftUINavigator.build/Debug/SwiftUINavigator.build/Objects-normal/arm64/NavBar.swiftdeps -serialize-diagnostics-path /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/Build/Intermediates.noindex/SwiftUINavigator.build/Debug/SwiftUINavigator.build/Objects-normal/arm64/NavBar.dia -target arm64-apple-macos10.15 -Xllvm -aarch64-use-tbi -enable-objc-interop -stack-check -sdk /users/redacted/Downloads/Xcode\ 13.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk -I /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/Build/Products/Debug -I /users/redacted/Downloads/Xcode\ 13.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib -F /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/Build/Products/Debug -F /users/redacted/Downloads/Xcode\ 13.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -enable-testing -g -module-cache-path /users/redacted/Library/Developer/Xcode/DerivedData/ModuleCache.noindex -profile-generate -profile-coverage-mapping -swift-version 5 -enforce-exclusivity\=checked -Onone -D SWIFT_PACKAGE -D DEBUG -D Xcode -new-driver-path /users/redacted/Downloads/Xcode\ 13.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-driver -serialize-debugging-options -Xcc -working-directory -Xcc /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator -resource-dir /users/redacted/Downloads/Xcode\ 13.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift -enable-anonymous-context-mangled-names -Xcc -I/users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/Build/Intermediates.noindex/SwiftUINavigator.build/Debug/SwiftUINavigator.build/swift-overrides.hmap -Xcc -I/users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/Build/Products/Debug/include -Xcc -I/users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/Build/Intermediates.noindex/SwiftUINavigator.build/Debug/SwiftUINavigator.build/DerivedSources-normal/arm64 -Xcc -I/users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/Build/Intermediates.noindex/SwiftUINavigator.build/Debug/SwiftUINavigator.build/DerivedSources/arm64 -Xcc -I/users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/Build/Intermediates.noindex/SwiftUINavigator.build/Debug/SwiftUINavigator.build/DerivedSources -Xcc -DSWIFT_PACKAGE -Xcc -DDEBUG\=1 -module-name SwiftUINavigator -target-sdk-version 12.3 -o /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/Build/Intermediates.noindex/SwiftUINavigator.build/Debug/SwiftUINavigator.build/Objects-normal/arm64/NavBar.o -index-unit-output-path /SwiftUINavigator.build/Debug/SwiftUINavigator.build/Objects-normal/arm64/NavBar.o -index-store-path /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/Index/DataStore -index-system-modules
    
    /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavBar/NavBar.swift:53:25: error: 'init(systemName:)' is only available in macOS 11.0 or newer
                            Image(systemName: "chevron.backward").foregroundColor(.blue)
                            ^
    /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavBar/NavBar.swift:53:25: note: add 'if #available' version check
                            Image(systemName: "chevron.backward").foregroundColor(.blue)
                            ^
    /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavBar/NavBar.swift:44:18: note: add @available attribute to enclosing instance method
        private func BarLeadingView() -> some View {
                     ^
    /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavBar/NavBar.swift:10:15: note: add @available attribute to enclosing generic struct
    public struct NavBar<Content: View>: View {
                  ^
    /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavBar/NavBar.swift:63:13: error: closure containing control flow statement cannot be used with result builder 'CommandsBuilder'
                if let view = background {
                ^
    SwiftUI.CommandsBuilder:4:30: note: struct 'CommandsBuilder' declared here
    @resultBuilder public struct CommandsBuilder {
                                 ^
    /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavBar/NavBar.swift:62:9: error: return type of instance method 'BarBackground()' requires that 'EmptyCommands' conform to 'View'
            Group {
            ^
    /users/redacted/Library/Developer/Xcode/DerivedData/MyProject-dnphidpimbleicharwshztaycdyg/SourcePackages/checkouts/SwiftUINavigator/SwiftUINavigator/Sources/SwiftUINavigator/NavBar/NavBar.swift:61:37: note: opaque return type declared here
        private func BarBackground() -> some View {
                                        ^~~~~~~~~
    
    image
    opened by bdrelling 3
  • Transition only fade in/out

    Transition only fade in/out

    By default transition should be set as A right-to-left slide transition on push, a left-to-right slide transition on pop But setting different types I receive only fade in/out. I want to achieve default ios transition.

    opened by ivan-kolesov 1
  • Cannot dismiss to a specific view

    Cannot dismiss to a specific view

    opened by ivan-kolesov 1
  • Support default navigator modifiers

    Support default navigator modifiers

    By default standard NavigationView supports such modifiers as navigationTitle or navigationBarTitleDisplayMode and etc. Would be better if the library will support those modifiers instead of custom navBar

    opened by ivan-kolesov 1
Releases(1.0.0)
Owner
OpenBytes
OpenBytes
An alternative SwiftUI NavigationView implementing classic stack-based navigation giving also some more control on animations and programmatic navigation.

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

Matteo 753 Jan 2, 2023
A wrapper for NavigationView and NavigationLink that makes programmatic navigation a little friendlier.

NavigatorKit A wrapper for NavigationView and NavigationLink that makes programmatic navigation a little friendlier. NavigatorKit is an opinionated wr

Gyuri Grell 2 Jun 16, 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
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
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
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
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
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