在回调,SwiftUI中以编程方式推送View

Boh*_*ych 6 ios swift swiftui

在我看来,Apple鼓励我们放弃UIViewController在SwiftUI中使用,但如果不使用视图控件,我会觉得有些无能为力。我想要的是能够实现某种ViewModel将事件发送给的功能View

ViewModel

public protocol LoginViewModel: ViewModel {
  var onError: PassthroughSubject<Error, Never> { get }
  var onSuccessLogin: PassthroughSubject<Void, Never> { get }
}
Run Code Online (Sandbox Code Playgroud)

查看

public struct LoginView: View {
  fileprivate let viewModel: LoginViewModel

  public init(viewModel: LoginViewModel) {
    self.viewModel = viewModel
  }

  public var body: some View {
    NavigationView {
      MasterView()
        .onReceive(self.viewModel.onError, perform: self.handleError(_:))
        .onReceive(self.viewModel.onSuccessLogin, perform: self.handleSuccessfullLogin)
    }
  }

  func handleSuccessfullLogin() {
    //push next screen
  }

  func handleError(_ error: Error) {
    //show alert
  }
}
Run Code Online (Sandbox Code Playgroud)

使用SwiftUI,我不知道如何实现以下功能:

  • 如果登录成功,则推送另一个控制器
  • 如果发生错误,则显示警报

此外,对于任何关于如何以更好的方式实现我想要的东西的建议,我也将不胜感激。谢谢。

更新1:我能够显示警报,但仍然找不到如何在viewModel的回调中推送另一个视图

Boh*_*ych 11

我找到了答案。如果要显示回调的其他视图,则应

1)创建状态 @State var pushActive = false

2)当ViewModel通知登录成功时,将其设置pushActivetrue

  func handleSuccessfullLogin() {
    self.pushActive = true
    print("handleSuccessfullLogin")
  }
Run Code Online (Sandbox Code Playgroud)

3)创建隐藏NavigationLink并绑定到该状态

  NavigationLink(destination: ProfileView(viewModel: ProfileViewModelImpl()), isActive: self.pushActive) {
    Text("")
  }.hidden()
Run Code Online (Sandbox Code Playgroud)

  • `Text("")` 在布局中为我添加了额外的空间,用 `EmptyView()` 替换它就成功了 (11认同)
  • 在第3步中,需要添加$用于绑定...即self.$pushActive (2认同)
  • 谢谢!虽然这工作得很好,但我认为苹果应该改变 SwiftUI 来实现这一点,而不需要诉诸于这种不直观的黑客以编程方式打开新视图。 (2认同)
  • isActive 在 IOS 16 中被弃用 (2认同)

Log*_*gan 7

我在这里添加了一些片段,因为我认为它简化了一些事情并使重用导航链接更容易:

1. 添加视图导航扩展

extension View {
    func navigatePush(whenTrue toggle: Binding<Bool>) -> some View {
        NavigationLink(
            destination: self,
            isActive: toggle
        ) { EmptyView() }
    }

    func navigatePush<H: Hashable>(when binding: Binding<H>,
                                   matches: H) -> some View {
        NavigationLink(
            destination: self,
            tag: matches,
            selection: Binding<H?>(binding)
        ) { EmptyView() }
    }

    func navigatePush<H: Hashable>(when binding: Binding<H?>,
                                   matches: H) -> some View {
        NavigationLink(
            destination: self,
            tag: matches,
            selection: binding
        ) { EmptyView() }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以调用任何视图(确保它们(或父视图)在导航视图中)

2. 闲暇时使用

struct Example: View {
    @State var toggle = false
    @State var tag = 0

    var body: some View {
        NavigationView {
            VStack(alignment: .center, spacing: 24) {
                Text("toggle pushed me")
                    .navigatePush(whenTrue: $toggle)
                Text("tag pushed me (2)")
                    .navigatePush(when: $tag, matches: 2)
                Text("tag pushed me (4)")
                    .navigatePush(when: $tag, matches: 4)

                Button("toggle") {
                    self.toggle = true
                }

                Button("set tag 2") {
                    self.tag = 2
                }

                Button("set tag 4") {
                    self.tag = 4
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)