Skip to content

Latest commit

 

History

History
524 lines (296 loc) · 10.1 KB

README.md

File metadata and controls

524 lines (296 loc) · 10.1 KB

Platform-iOS Platform-macOS License: MIT

NavigationStackEX

Abstract encapsulation of the default NavigationStack with custom functionalities aimed at simplifying SwiftUI view management and navigation.

Push, present, and pop your views elegantly, helping you reduce time, complexity, and maintain code clarity.

And it's Super Lightweight!

screenshot screenshot

Features

✅ 100% SwiftUI

✅ Powerful solution for your Navigation

✅ Super easy-to-use

✅ Lightweight

✅ Supports any kind of Views

✅ Can dynamically push / present your Views in a SwiftUI

✅ Can predefine the pre-loading of your views in the app, ahead of use, in a more conventional way if needed

⚠️ Known limitation: The display of modal views (for 'present' and 'presentFullScreen') are not stackable as of right now (will be in the future)

Requirements

  • iOS 16.0+, macOS 14.0+
  • SwiftUI 5.0+

Installation

Swift Package Manager:

XCode > File > Add Package Dependency...

Enter the following URL of the repository into the search:

https://github.com/MaximeFILIPPI/NavigationStackEX

Follow the prompts to complete the installation.


Setup

Here is an example of how to integrate and use NavigationStackEX in your SwiftUI project:

import SwiftUI
import NavigationStackEX // <- Import
    
@main
struct YourProject: App {
    
    var body: some Scene {
    
        WindowGroup {
        
            NavigationStackEX { // <- use THIS instead of NavigationStack
            
                ContentView()
                
            }
            
        }
        
    }
    
}

Basic Usage

To navigate between your views, simply add @EnvironmentObject var navigator: Navigator Then use the navigator functions as push, present, presentFullScreen, pop, popToRoot, dismiss to trigger the navigation:

import SwiftUI
import NavigationStackEX // <- Import

struct ContentView: View {

    @EnvironmentObject var navigator: Navigator  // <- Add this line to the view
    
    var body: some View {
    
        VStack {
        
            Button("Go to Profile") {
            
                self.goToNextScreen()
            
            }
            
        }
        
    }
    
    
    func goToNextScreen()
    {
        navigator.push(to: ProfileView()) // <- use THIS to navigate
    }
    
}

THAT'S IT!


Detailed Navigation Usage

PUSH

Navigate to next screen

// Navigate to a SwiftUI view instance
navigator.push(to: ProfileView())

POP

Back to previous screen

// Back to the previous SwiftUI view
navigator.pop()

Back to the root of your navigation

// Back to the very first SwiftUI view
navigator.popToRoot()

Back to a specific screen in the stack

Note: You must use the parameter "identifier" when navigating to activate this functionnality (example: navigator.push(to: ProfileView(), identifier: "profile")))

// Back to a specified SwiftUI view
navigator.pop(to: "profile") // <- will take you back to the profile view in your navigation stack

PRESENT

Note: Be careful the present modal way of displaying views is not stackable at the moment.

// Navigate to a SwiftUI view instance
navigator.present(ProfileView())

PRESENT FULL SCREEN

Cover opening full screen

// Navigate to a SwiftUI view instance
navigator.presentFullScreen(ProfileView())

DISMISS

Close a modal that has been present or presentFullScreen:

// Navigate to a destination 
navigator.dismiss()

Passing Data

How to pass data? Just like any SwiftUI views:

// Set data using a view instance
navigator.push(to: ProfileView(name: "Max"))

Customization

BACK BUTTON

You can change the back button of the navigation bar if you wish:

import SwiftUI
import NavigationStackEX

struct TempScreenView: View {
    
    var body: some View {
        ZStack {
            Color.red
        }
        .navigationBackButtonItem(Text("Back")) // Customize back button if needed (can be any view)
    }
}

RIGHT ITEM / LEFT ITEM

or the left / right navigation bar items :

import SwiftUI
import NavigationStackEX

struct TempScreenView: View {
    
    var body: some View {
        ZStack {
            Color.red
        }
        .navigationLeftViewItem(YourOwnCustomBackButtonView()) // <- Right navigation bar button (your own custom view)
        .navigationRightViewItem(YourOwnCustomLogoView()) // <- Left navigation bar button (your own custom view)
    }
}

LARGE TITLE

Change the way the title is diplayed (Large or Inline)

import SwiftUI
import NavigationStackEX

struct TempScreenView: View {
    
    var body: some View {
        ZStack {
            Color.red
        }
        .navigationTitle("Title")
        .navigationBarHidden(false)
        .toolbarTitleDisplayMode(.large) // Large or Inline for better user experience (avoid using automatic)
    }
}

HIDDEN TITLE

or not displayed at all and hide the navigation bar:

import SwiftUI
import NavigationStackEX

struct TempScreenView: View {
    
    var body: some View {
        ZStack {
            Color.red
        }
        .navigationBarTitle("")
        .navigationBarHidden(true)
    }
}

(OPTIONAL) TRADITIONAL NAV STYLE

For people that prefers to keep everything tidy in one class only, you can use a more traditional approach to navigate by declaring and pre-loading the screens (SwiftUI views) that you need before using them.

import SwiftUI
import NavigationStackEX // <- Import
    
@main
struct YourProject: App {
    
    @State var segues: [String: AnyView] = [:] // <- Add this line to prepare destinations
    
    var body: some Scene {
    
        WindowGroup {
        
            NavigationStackEX(destinations: $segues) { // <- The NavigationEX
                ContentView() // <- Your root view (first view)
            }
            .onAppear {
                self.setupNavigation()
            }
        }
    }
    
    
    
    // You can setup the destinations ahead of time 
    // By predefining your views with associated tags
    // It allows you to navigate in a more classical way by referencing tags instead of views instance
    
    func setupNavigation()
    {
        // Example for a profile view in your app
        self.segues["profile"] = ProfileView().any // <- add .any modifier behind your view
        
        // Add more segues as needed
    }
}

Then use the functions like this:

PUSH

// Navigate to a destination from a tag
navigator.push(to: "profile")

POP

// Back to the previous SwiftUI view
navigator.pop()

// Back to the very first SwiftUI view
navigator.popToRoot()

// Back to a specified SwiftUI view
navigator.pop(to: "profile") // <- will take you back to the profile view of your app, wherever it is in your navigation stack

PRESENT

// Navigate to a destination 
navigator.present("profile")

PRESENT FULL SCREEN

// Navigate to a destination 
navigator.presentFullScreen("profile")

DISMISS

Close a modal that has been present or presentFullScreen:

// Navigate to a destination 
navigator.dismiss()

PASSING / RETRIEVING DATA

How to pass data:

// Set data using a destination (tag reference)
let data: String = "Max" // <- Can be any type of data, primivite, objects, etc...
navigator.push(to: "profile", with: data)

Get the data:

navigator.data(for: "profile") as? String // <- Can be any type of data, primivite, objects, etc...

Profile class example when retrieving data:

struct ProfileView: View {

    @State var name: String = ""
    
    var body: some View {
    
        ZStack {
        
            Text(name) 
            
        }
        .onAppear {
            loadInfos()
        }
        
    }
    

    func loadInfos()
    {
        if let profileData = navigator.data(for: "profile") as? String // <- Get it here
        {
            name = profileData
        }
    }
}

License

SnapPager is available under the MIT license. See the LICENSE file for more details.


Credits

NavigationStackEX is developed and maintained by [Maxime FILIPPI].

If you encounter any issues or have suggestions for improvements, please open an issue.