Issue #824
Use a custom NavigationLink
with EmptyView
as the background, this failable initializer accepts Binding of optional value. This works well as the destination are made lazily.
extension NavigationLink where Label == EmptyView {
init?<Value>(
_ binding: Binding<Value?>,
@ViewBuilder destination: (Value) -> Destination
) {
guard let value = binding.wrappedValue else {
return nil
}
let isActive = Binding(
get: { true },
set: { newValue in if !newValue { binding.wrappedValue = nil } }
)
self.init(destination: destination(value), isActive: isActive, label: EmptyView.init)
}
}
extension View {
@ViewBuilder
func navigate<Value, Destination: View>(
using binding: Binding<Value?>,
@ViewBuilder destination: (Value) -> Destination
) -> some View {
background(NavigationLink(binding, destination: destination))
}
}
Then in our OnboardView, we can check the destination and make according View
final class OnboardViewModel: ObservableObject {
enum Destination {
case email
case confirm
case avatar
}
@Published var destination: Destination? = .email
func goTo(destination: Destination) {
self.destination = destination
}
}
struct OnboardView: View {
@StateObject private var viewModel = OnboardViewModel()
var body: some View {
NavigationView {
OnboardEmailView(viewModel: viewModel)
.navigate(using: $viewModel.destination, destination: makeView)
}
}
@ViewBuilder
private func makeView(destination: OnboardViewModel.Destination) -> some View {
switch destination {
case .email:
OnboardEmailView(viewModel: viewModel)
case .confirm:
OnboardConfirmView(viewModel: viewModel)
case .avatar:
OnboardAvatarView(viewModel: viewModel)
}
}
}
Reference
- Lazy navigation in SwiftUI https://swiftwithmajid.com/2021/01/27/lazy-navigation-in-swiftui/