Il me semble que Apple nous encourage à abandonner l'utilisation de UIViewController
dans SwiftUI, mais sans utiliser les contrôles de vue, je me sens un peu impuissant. Ce que je voudrais, c'est de être capable d'implémenter une sorte de ViewModel
qui émettra des événements vers View
.
ViewModel:
public protocol LoginViewModel: ViewModel {
var onError: PassthroughSubject<Error, Never> { get }
var onSuccessLogin: PassthroughSubject<Void, Never> { get }
}
Voir:
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
}
}
En utilisant SwiftUI, je ne sais pas comment implémenter les éléments suivants:
Aussi, j'apprécierais tout conseil sur la façon de mettre en œuvre ce que je veux d'une meilleure manière. Merci.
pdate 1: J'ai pu afficher une alerte, mais je ne trouve toujours pas comment pousser une autre vue dans le rappel de viewModel
comme @Bhodan l'a mentionné, vous pouvez le faire en changeant d'état
Utilisation de EnvironmentObject avec SwiftUI
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
}
la propriété loggedIn
sera utilisée pour surveiller quand un changement d'utilisateur se connecte ou se déconnecte
@EnvironmentObject
dans votre SceneDelegate.Swift
fichier dans Xcode, ce qui le rend juste accessible partout dans votre applicationclass 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()
}
}
Une fois que vous avez modifié la propriété loggedIn
, toute interface utilisateur qui lui est liée répondra au changement de valeur vrai/faux
le @Bhodan mentionné l'ajoute simplement à votre vue et il répondra à ce changement
struct LoginView: View {
@EnvironmentObject userData: UserData
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: ProfileView(), isActive: self.$userData.loggedin) {
Text("")
}.hidden()
}
}
}
}
Solution sans créer supplémentaire vues vides.
Vous pouvez utiliser les modificateurs .disabled(true)
ou .allowsHitTesting(false)
pour désactiver les taps sur NavigationLink.
Inconvénient: vous perdez le bouton par défaut en surbrillance.
NavigationLink(destination: EnterVerificationCodeScreen(), isActive: self.$viewModel.verifyPinIsShowing) {
Text("Create an account")
}
.allowsHitTesting(false) // or .disabled(true)
.buttonStyle(ShadowRadiusButtonStyle(type: .dark, height: 38))