|
// |
|
// Created by Kris McGinnes on 2/6/20. |
|
// Copyright © 2020 Spicy Taco. All rights reserved. |
|
// |
|
|
|
import SwiftUI |
|
|
|
/// A type that allows future navigation within an `ActionNavigationLink` context. |
|
struct NavigationToken { |
|
private let navigationAction: () -> () |
|
|
|
fileprivate init(_ action: @escaping () -> ()) { |
|
self.navigationAction = action |
|
} |
|
|
|
/// Performs navigation immediately. |
|
func navigate() { |
|
navigationAction() |
|
} |
|
} |
|
|
|
/// Similar to a normal `NavigationLink`, but allows arbitrary code to be executed before explicitly allowing navigation to proceed. |
|
/// |
|
/// This allows delays to be added, models to be updated, or conditionals to be checked before performing, or not performing, the actual naviation. |
|
struct ActionNavigationLink<Destination: View, Content: View>: View { |
|
private let destination: Destination |
|
private let content: Content |
|
private let action: (NavigationToken) -> () |
|
|
|
@State private var continueToDestination: Bool = false |
|
|
|
init( |
|
destination: Destination, |
|
action: @escaping (NavigationToken) -> (), |
|
@ViewBuilder content: () -> Content |
|
) { |
|
self.destination = destination |
|
self.action = action |
|
self.content = content() |
|
} |
|
|
|
var body: some View { |
|
Button(action: buttonAction, label: { content }) |
|
.background( |
|
NavigationLink( |
|
destination: destination, |
|
isActive: $continueToDestination, |
|
label: { EmptyView() } |
|
) |
|
) |
|
} |
|
|
|
private func buttonAction() { |
|
let token = NavigationToken { |
|
self.continueToDestination = true |
|
} |
|
|
|
action(token) |
|
} |
|
} |
|
|
|
struct ActionNavigationLink_Previews: PreviewProvider { |
|
static var previews: some View { |
|
NavigationView { |
|
VStack(spacing: 32) { |
|
// This behaves exactly as a `NavigationLink` would |
|
ActionNavigationLink( |
|
destination: EmptyView(), |
|
action: { $0.navigate() } |
|
) { |
|
Text("Immediate") |
|
} |
|
|
|
// This adds a 3 second delay, simulating an API call, then navigates |
|
ActionNavigationLink( |
|
destination: EmptyView(), |
|
action: { token in |
|
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) { |
|
token.navigate() |
|
} |
|
} |
|
) { |
|
Text("Delayed") |
|
} |
|
} |
|
.navigationBarTitle("Examples") |
|
} |
|
} |
|
} |