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.

GitHub

View Github