Swift UI 生命周期:如何用新视图替换当前视图?不是简单地推动它

Swift UI Lifecycle: how to replace the current view with a new one ? Not simply pushing it

提问人:dave.rain 提问时间:1/28/2022 最后编辑:dave.rain 更新时间:4/11/2022 访问量:1341

问:

我有一个按照 SwiftUI 生命周期创建的 SwiftUI 应用程序。我能够毫无问题地推送新视图,但我无法弄清楚如何替换视图。 我需要实现的是我在 AppDelegate 生命周期中使用此代码能够执行的操作

let bounds = UIScreen.main.bounds
self.window = UIWindow(frame: bounds)
self.window?.rootViewController = rootViewController
self.window?.makeKeyAndVisible()

我尝试替换根视图并在推送新视图时关闭旧视图,但我找不到有效的解决方案。

我试着用

presentationMode.wrappedValue.dismiss()

但是,如果我试图在我推送新视图之前或之后关闭视图,则不起作用。

我不知道我是否错过了什么。

编辑

我尝试使用ViewGroup实现解决方案

import SwiftUI

// Specify all root level screens
enum Screen {
    case login
    case users
}

@main
struct IosstarterkitApp: App {
    var loginV: AnyView
    var userV: AnyView
    // Use @State for current screen
    @State var screen: Screen = .login

    init() {

        // instantiate the factory for the dependency injection
        let baseProviderFactory: BaseProviderProtocol = BaseProviderFactory()
        
        // Create the SwiftUI views that provides the window contents.
        var userView = UserView(store: UserStore())
        userView.setPresenter(presenter: baseProviderFactory.getUserPresenter() as! BasePresenterProtocol)
        userV = AnyView(userView)
        var loginView = LoginView(store: LoginStore())
        loginView.setPresenter(presenter: baseProviderFactory.getLoginPresenter() as! BasePresenterProtocol)
        loginV = AnyView(loginView)

        let userDefaultRepository = UserDefaultsRepository()
        if userDefaultRepository.getAccessToken() != nil {
            self.screen = .users
        } else {
            self.screen = .login
        }
    }

    var body: some Scene {
        WindowGroup {
            switch screen {
            case .login:
                RootView(view: loginV)
            case .users:
                RootView(view: userV)
            }
        }
    }
}

问题是,当我尝试根据令牌的存在更新屏幕变量时,该变量没有得到更新。我正在尝试根据用户被记录的条件显示视图或其他视图。 此外,我需要从另一个文件中的另一个视图(如 LoginView)更新 screen 变量。我该怎么做?

iOS Swift SwiftUI

评论

0赞 Ptit Xav 1/28/2022
尝试使用状态变量(枚举或布尔值),您可以使用它来选择要显示的视图。
0赞 Stefan 1/28/2022
尝试阅读有关@State的更多信息:swiftui-lab.com/state-changes

答:

5赞 Andrew Bogaevskyi 1/28/2022 #1

我看到您要替换应用程序的根视图。下面是如何使用 SwiftUI App 生命周期的简单示例:


import SwiftUI

// Specify all root level screens
enum Screen {
    case onboarding
    case home
}

@main
struct MyApp: App {
    // Use @State for current screen
    @State var screen: Screen = .onboarding

    var body: some Scene {
        WindowGroup {
            switch screen {
            case .onboarding:
                OnboardingView {
                    screen = .home
                }
            case .home:
                HomeView()
            }
        }
    }
}

struct OnboardingView: View {
    let openHome: () -> Void

    var body: some View {
        VStack {
            Text("OnboardingView")
            Button("Open Home") {
                openHome()
            }
        }
    }
}

struct HomeView: View {
    var body: some View {
        Text("HomeView")
    }
}

您可以将 视为常规视图,您可以在其中使用 state 替换任何根视图。MyApp: AppViewSwiftUI

编辑

这是代码的更新版本。它使用了 Coordinator 的非常基本的实现。这里 Coordinator 也是一个用于创建视图的工厂。有关更高级的信息,请搜索特定于 SwiftUI 的 MVVM with Coordinator。


enum Screen {
    case login
    case users
}

final class AppCoordinator: ObservableObject {
    @Published var screen: Screen = .login
    private let userDefaultRepository = UserDefaultsRepository()
    private let providerFactory: BaseProviderProtocol = BaseProviderFactory()

    init() {
        if userDefaultRepository.getAccessToken() != nil {
            screen = .users
        } else {
            screen = .login
        }
    }

    func userView() -> some View {
        let store = UserStore()
        let presenter = baseProviderFactory.getUserPresenter() as! BasePresenterProtocol
        return UserView(store: store, presenter: presenter)
    }

    func loginView() -> some View {
        let store = LoginStore()
        let presenter = baseProviderFactory.getLoginPresenter() as! BasePresenterProtocol
        return LoginView(store: LoginStore(), presenter: presenter)
    }
}

@main
struct IosstarterkitApp: App {
    @StateObject var coordinator = AppCoordinator()

    var body: some Scene {
        WindowGroup {
            switch coordinator.screen {
            case .login:
                coordinator.loginView()
            case .users:
                coordinator.userView()
            }
        }
    }
}

评论

0赞 dave.rain 2/1/2022
谢谢你的回答。我已经找到了这个解决方案,并根据您的建议更新了问题描述。问题是我很难尝试使它适应我的代码。你有什么建议吗?
0赞 Andrew Bogaevskyi 2/1/2022
@dave.rain 您正在尝试应用 UIKit 中的方法,将视图存储在 properties 和 中。这不是 SwiftUI 的方式。让我更新一下我的答案。我不知道 UserView 和 LoginView 的实现,但最好避免使用这样的属性。为此,您可以使用 init 注入 use a ViewModel。UserViewsetPresenter
0赞 dave.rain 2/2/2022
谢谢。我在想这个问题,但我不确定。我会尝试这种方法。
0赞 dave.rain 2/6/2022
它正在工作。多谢。