在我看来,Apple鼓励我们放弃在SwiftUI中使用UIViewController
,但是如果不使用视图控件,我会觉得有些无能为力。我想要的是能够实现某种ViewModel
,它将向View
发出事件。
ViewModel :
public protocol LoginViewModel: ViewModel {
var onError: PassthroughSubject<Error, Never> { get }
var onSuccessLogin: PassthroughSubject<Void, Never> { get }
}
查看:
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
}
}
使用SwiftUI,我不知道如何实现以下目标:
此外,我希望您能以更好的方式实现自己想要的任何建议。谢谢。
更新1:我能够显示警报,但仍然找不到如何在viewModel的回调中推送另一个视图
答案 0 :(得分:15)
我找到了答案。如果要在回调中显示其他视图,则应
1)创建状态@State var pushActive = false
2)当ViewModel通知登录成功时,将pushActive
设置为true
func handleSuccessfullLogin() {
self.pushActive = true
print("handleSuccessfullLogin")
}
3)创建隐藏的NavigationLink
并绑定到该状态
NavigationLink(destination: ProfileView(viewModel: ProfileViewModelImpl()), isActive: self.pushActive) {
Text("")
}.hidden()
答案 1 :(得分:2)
正如@Bhodan提到的,您可以通过更改状态来做到这一点
在SwiftUI中使用 EnvironmentObject
class UserData: ObservableObject, Identifiable {
let id = UUID()
@Published var firebase_uid: String = ""
@Published var name: String = ""
@Published var email: String = ""
@Published var loggedIn: Bool = false
}
loggedIn
属性将用于监视用户更改何时登录或注销
@EnvironmentObject
文件中的SceneDelegate.swift
这样就可以使其在您的应用中的任何地方都可以访问class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
// Create the SwiftUI view that provides the window contents.
let userData = UserData()
let contentView = ContentView().environmentObject(userData)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
对loggedIn
属性进行任何更改后,绑定到该UI的任何UI都将响应true / false值
@Bhodan提到的只是将其添加到您的视图中,它将响应该更改
struct LoginView: View {
@EnvironmentObject var userData: UserData
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: ProfileView(), isActive: self.$userData.loggedin) {
EmptyView()
}.hidden()
}
}
}
}
答案 2 :(得分:1)
从beta 5开始,NavigationLink是用于以编程方式推送视图的机制。您可以看到它的一个示例here。
答案 3 :(得分:0)
解决方法,而无需创建其他空白视图。
您可以使用.disabled(true)
或.allowsHitTesting(false)
修饰符来禁用NavigationLink上的点击。
缺点:您松开了默认按钮的点击突出显示。
NavigationLink(destination: EnterVerificationCodeScreen(), isActive: self.$viewModel.verifyPinIsShowing) {
Text("Create an account")
}
.allowsHitTesting(false) // or .disabled(true)
.buttonStyle(ShadowRadiusButtonStyle(type: .dark, height: 38))
答案 4 :(得分:0)
我在此处添加一些代码段,因为我认为它可以简化某些操作并使重用导航链接更容易:
1。添加查看导航扩展
ComputeHash(input[], N)
For hash-input input[] of length N bytes (8N bits) and a random secret key K of 320 bits
Result = 0;
For each bit b in input[] {
if (b == 1) then Result ^= (left-most 32 bits of K);
shift K left 1 bit position;
}
return Result;
现在,您可以在任何视图上调用(确保他们(或父母)在导航视图中)
2。休闲使用
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() }
}
}