Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
226 views
in Technique[技术] by (71.8m points)

Pushing multiple navigation links from a parent view in SwiftUI

I want to implement a wizard whereby the user has to go through multiple screens in order to complete a signup process.

In SwiftUI the easiest way to do this is to have each view when it's finished push the next view on the navigation stack, but this codes the entire navigation between views in the views themselves, and I would like to avoid it.

What I want to do is have a parent view show the navigation view and then push the different steps on that navigation view.

I have something working already that looks like this:

struct AddVehicleView: View {
    @ObservedObject var viewModel: AddVehicleViewModel

    var body: some View {
        NavigationView {
            switch viewModel.state {
            case .description:
                AddDescriptionView(addDescriptionViewModel: AddVehicleDescriptionViewModel(), addVehicleViewModel: viewModel)
            case .users:
                AddUsersView(viewModel: AddUsersViewModel(viewModel.vehicle), addVehicleViewModel: viewModel)
            }
        }
    }
}

This works fine. In the first step the AddVehicleViewModel is updated with the necessary info, the AddVehicleView is re-evaluated, the switch case jumps to the next option and the next view is presented to complete the wizard.

The issue with this however is that there are no navigation stack animations. Views simply get replaced. How can I change this to a system whereby the views are pushed, without implementing the push inside the AddDescriptionView object?

Should I write wrapper views that do the navigation stack handling on top of those views, and get rid of the switch case?

question from:https://stackoverflow.com/questions/65916888/pushing-multiple-navigation-links-from-a-parent-view-in-swiftui

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Ok so if you want to go from view a to b you should implement this not in your NavigationView but the view after the NavigationView, this way you wont break the animations. Why? Good question, I really don't know. When possible I keep my NavigationView always in the App struct under WindowGroup.

To get back to the point. Basically there should be an intermediate view between your steps and NavigationView. This view (StepperView) will contain the navigation logic of your steps. This way you keep the animations intact.

import SwiftUI

class AddVehicleViewModel: ObservableObject {
    
    enum StateType {
        case description
        case users1
        case users2
    }
    
    
    @Published var state: StateType? = nil
    
}

struct AddDescriptionView: View {

    @ObservedObject var viewModel: AddVehicleViewModel
    
    @State var text: String = ""
    var body: some View {
        GeometryReader {proxy in
            VStack {
                TextField("test", text: self.$text).background(RoundedRectangle(cornerRadius: 10).fill(Color.white).frame(width: 150, height: 40)).padding()
                Button("1") {
                    viewModel.state = .users1
                }
            }.frame(width: proxy.size.width, height: proxy.size.height, alignment: .center).background(Color.orange)
        }
    }
}

struct AddUsersView: View {

    @ObservedObject var viewModel: AddVehicleViewModel
    
    
    var body: some View {
        GeometryReader {proxy in
            ZStack {
                Button("2") {
                    viewModel.state = .users2
                }
            }.frame(width: proxy.size.width, height: proxy.size.height, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/).background(Color.orange)
        }
    }
}

struct AddUsersView2: View {

    @ObservedObject var viewModel: AddVehicleViewModel
    
    
    var body: some View {
        GeometryReader {proxy in
            ZStack {
                Button("3") {
                    viewModel.state = .description
                }
            }.frame(width: proxy.size.width, height: proxy.size.height, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/).background(Color.orange)
        }
    }
}

struct StepperView: View {
    
    @ObservedObject var viewModel: AddVehicleViewModel = AddVehicleViewModel()
    
    var body: some View {
        VStack {
            NavigationLink(
                destination: AddDescriptionView(viewModel: viewModel),
                isActive: .constant(viewModel.state == .description),
                label: {EmptyView()})
            if viewModel.state == .users1 {
                NavigationLink(
                    destination: AddUsersView(viewModel: viewModel),
                    isActive: .constant(true),
                    label: {EmptyView()})
            }
            if viewModel.state == .users2 {
                NavigationLink(
                    destination: AddUsersView2(viewModel: viewModel),
                    isActive: .constant(true),
                    label: {EmptyView()})
            }
        }.onAppear {
            viewModel.state = .description
        }
    }
}

class BackBarButtonItem: UIBarButtonItem {
    @available(iOS 14.0, *)
    override var menu: UIMenu? {
        set {
            // Don't set the menu here
            // super.menu = menu
        }
        get {
            return super.menu
        }
    }
}

struct AddVehicleView: View {
    @ObservedObject var viewModel: AddVehicleViewModel = AddVehicleViewModel()
    
    var body: some View {
        NavigationView {
            NavigationLink(
                destination: StepperView(),
                isActive: .constant(true),
                label: {EmptyView()})
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...