web-dev-qa-db-fra.com

Push View par programmation dans le rappel, SwiftUI

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:

  • Poussez un autre contrôleur si la connexion réussit
  • Afficher l'alerte si l'erreur s'est produite

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

16
Bohdan Savych

comme @Bhodan l'a mentionné, vous pouvez le faire en changeant d'état

Utilisation de EnvironmentObject avec SwiftUI

  1. Ajouter UserData ObservableObject:
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

  1. Ajoutez-le maintenant en tant que @EnvironmentObject dans votre SceneDelegate.Swift fichier dans Xcode, ce qui le rend juste accessible partout dans votre application
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()
        }
    }

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()
   }
  }
 }
}
0
Vybz Kartel

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))
0
Igor Kasuan