web-dev-qa-db-ja.com

SwiftUIのコールバックでプログラムによってビューをプッシュする

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を使用して、私は次の実装方法がわかりません。

  • ログインが成功した場合、別のコントローラーをプッシュします
  • エラーが発生した場合にアラートを表示する

また、私がより良い方法で私が欲しいものを実装する方法についてのアドバイスをいただければ幸いです。ありがとう。

pdate 1:アラートを表示できましたが、viewModelのコールバックで別のビューをプッシュする方法が見つかりません

16
Bohdan Savych

@Bhodanが述べたように、状態を変更することでそれを行うことができます

EnvironmentObject with SwiftUIの使用

  1. 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
}

loggedInプロパティは、ユーザーの変更がいつログインまたはログアウトするかを監視するために使用されます

  1. Xcodeの@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がtrue/false値の変更に応答します。

@Bhodanが言及したように、これをビューに追加するだけで、その変更に応答します


struct LoginView: View {
@EnvironmentObject userData: UserData

var body: some View {
NavigationView {
VStack {
NavigationLink(destination: ProfileView(), isActive: self.$userData.loggedin) {
    Text("")
    }.hidden()
   }
  }
 }
}
0
Vybz Kartel

回避策作成せずに追加空のビュー。

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