使用 Combine 路由 SwiftUI 视图

Use Combine to route SwiftUI Views

我正在尝试使用 Combine 来路由 SwiftUI 视图,它有点工作,但有一些不需要的行为,我希望有人可以帮助我。我制作了一个 example project,它只包含两个文件,ViewRouter.swiftViewController.swift,只包含一个 @IBSegueAction 到 return 一个 UIHostingController:

class ViewRouter 是包含 @ViewBuilder 属性 的 SwiftUI 视图的 ViewModel。当SwiftUI视图调用didFinish()函数时,step属性设置为下一个View.

class ViewRouter: ObservableObject {
    enum Step { case test1, test2, test3, test4 }
    @Published var step: Step = .test1
    @ViewBuilder var nextStepView: some View {
        switch step {
        case .test1:
            Test1(router: self)
        case .test2:
            Test2(router: self)
        case .test3:
            Test3(router: self)
        case .test4:
            Test4(router: self)
        }
    }
    
    func didFinish() {
        switch step {
        case .test1:
            step = .test2
        case .test2:
            step = .test3
        case .test3:
            step = .test4
        case .test4:
            break
        }
    }
}

这非常适合 Test1,当点击 Next link 时,Test1 视图向左动画显示 Test2。但是,当在 Test2 中点击 Next 时,Test2 会向左移动以显示 Test3,但随后它会立即向右移动以显示相同的 [=30] =] 视图。在 Test3.

中点击 Next 时会发生完全相同的情况

并且点击后退按钮总是在 Test1 视图中结束。

除了Text中的文字外,所有四个视图都相同:

struct Test1: View {
    @ObservedObject var router: ViewRouter
    @State var nextView = false

    var body: some View {
        VStack {
            NavigationLink(destination: router.nextStepView, isActive: $nextView) {
                EmptyView()
            }

            Text("Test1")
            
            Button(action: {
                nextView = true
                router.didFinish()
            }, label: {
                Text("Next")
            })
        }
    }
}

我对 ViewRouter class 进行了一些小改动以使其正常工作。我得出的结论是所有 SwiftUI 视图都需要它们自己的 ViewRouter 实例。对所有视图使用相同的 ViewRouter 共享实例会导致不良行为。我引入了一个 nextStep 可选 属性 ,当视图调用 didFinish 函数时设置为当前步骤,以确保路由器 returns 正确 nextStepView 如果调用第二次或第三次等等。

我已经为示例 Xcode 项目推送了对 GitHub 存储库的更新。

修改后的 class 现在看起来像这样:

class ViewRouter: ObservableObject {
    enum Step { case test1, test2, test3, test4 }
    @Published var step: Step
    private var nextStep: Step?
    @ViewBuilder var nextStepView: some View {
        switch step {
        case .test1:
            Test1(router: self)
        case .test2:
            Test2(router: ViewRouter(step: .test2))
        case .test3:
            Test3(router: ViewRouter(step: .test3))
        case .test4:
            Test4(router: ViewRouter(step: .test4))
        }
    }
    
    init(step: Step) {
        self.step = step
    }
    
    func didFinish() {
        switch step {
        case .test1:
            if let next = nextStep {
                step = next
            } else {
                step = .test2
                nextStep = .test2
            }
        case .test2:
            if let next = nextStep {
                step = next
            } else {
                step = .test3
                nextStep = .test3
            }
        case .test3:
            if let next = nextStep {
                step = next
            } else {
                step = .test4
                nextStep = .test4
            }
        case .test4:
            break
        }
    }
    
}