Skip to content

Instantly share code, notes, and snippets.

@moyerr
Created July 7, 2023 17:17
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moyerr/ebac14388bf38cb9962dcf33c06032b8 to your computer and use it in GitHub Desktop.
Save moyerr/ebac14388bf38cb9962dcf33c06032b8 to your computer and use it in GitHub Desktop.
A demonstration of navigation in SwiftUI using NavigationStack and Environment actions
import SwiftUI
// MARK: Navigation
enum Screen: String, Hashable, CaseIterable, CustomStringConvertible {
case first, second, third
var description: String {
rawValue.capitalized
}
}
struct MainView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
List(Screen.allCases, id: \.self) { screen in
NavigationLink(screen.description, value: screen)
}
.navigationTitle("Home")
.navigationDestination(for: Screen.self) { screen in
Router(screen: screen)
}
}
.environment(\.push, .init { path.append($0) })
.environment(\.pop, .init { path.removeLast() })
.environment(\.popToRoot, .init { path = NavigationPath() })
}
}
struct Router: View {
let screen: Screen
var body: some View {
switch screen {
case .first:
FirstScreen()
case .second:
SecondScreen()
case .third:
ThirdScreen()
}
}
}
// MARK: Screens
struct FirstScreen: View {
@Environment(\.pop) private var pop
var body: some View {
VStack {
NavigationLink("Next Screen", value: Screen.second)
Button("Go back") {
pop()
}
}
.buttonStyle(.borderedProminent)
.navigationTitle(Screen.first.description)
}
}
struct SecondScreen: View {
@Environment(\.pop) private var pop
@Environment(\.push) private var push
var body: some View {
VStack {
Button("Push after delay") {
Task {
try? await Task.sleep(for: .seconds(0.5))
push(Screen.third)
}
}
Button("Go back") {
pop()
}
}
.buttonStyle(.borderedProminent)
.navigationTitle(Screen.second.description)
}
}
struct ThirdScreen: View {
@Environment(\.popToRoot) private var popToRoot
var body: some View {
Button("Go back home") {
popToRoot()
}
.navigationTitle(Screen.third.description)
}
}
// MARK: Environment Actions
struct PushAction {
let action: (any Hashable) -> Void
init(_ action: @escaping (any Hashable) -> Void) {
self.action = action
}
func callAsFunction(_ value: any Hashable) {
action(value)
}
}
struct PopAction {
let action: () -> Void
init(_ action: @escaping () -> Void) {
self.action = action
}
func callAsFunction() {
action()
}
}
extension EnvironmentValues {
private enum PushActionKey: EnvironmentKey {
static var defaultValue = PushAction { _ in
debugPrint("Push action was invoked, but no value is set")
}
}
private enum PopActionKey: EnvironmentKey {
static var defaultValue = PopAction {
debugPrint("Pop action was invoked, but no value is set")
}
}
private enum PopToRootActionKey: EnvironmentKey {
static var defaultValue = PopAction {
debugPrint("Pop to root action was invoked, but no value is set")
}
}
var push: PushAction {
get { self[PushActionKey.self] }
set { self[PushActionKey.self] = newValue }
}
var pop: PopAction {
get { self[PopActionKey.self] }
set { self[PopActionKey.self] = newValue }
}
var popToRoot: PopAction {
get { self[PopToRootActionKey.self] }
set { self[PopToRootActionKey.self] = newValue }
}
}
@mikemike396
Copy link

This looks amazing! Thank you for sharing! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment