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 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
- Import
SwiftUINavigator
.
import SwiftUINavigator
- Declare
NavigatorView
in the root view of the app.
NavigatorView {
HomeScreen()
}
NavigatorView
supports transition animations and other options.
See NavigatorView
- 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 about
Navigator
, see NavigatorLink
- Or using
Navigator
@EnvironmentObject private var navigator: Navigator
navigator.navigate(SomeView())
For more details about
Navigator
, see Navigator
- Dismiss (navigate back) to the previous view
programmatically (usingNavigator
) or using a link (usingDismissLink
).
- 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 aNavigatorView
other than the root one.
Keep in mind that if you didn’t pass theNavigator
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 ofNavigator
using theEnvironmentObject
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
- 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)
- Present sheet
navigator.navigate(ProductDetailScreen(item: item), type: .sheet)
- 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
supportsnormal
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 LicenseLicensed 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.