Completely move to other view and don't allow to go back in SwiftUI

前端 未结 2 581
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-13 03:36

I\'m developing a simple iOS app with SwiftUI with two views: a LogInView() and a HomeView().

What I want is really simple: when the user c

相关标签:
2条回答
  • 2021-01-13 04:03

    Update: I got some time to update the answer, and add a transition. Note that I changed Group by VStack, otherwise the transition does not work.

    You can alter the duration in the withAnimationcall (button closure).

    I also moved some modifiers in your button, so the whole thing is "tappable". Otherwise, only tapping on the text of the button would trigger the action.


    You can use an ObservedObject, an EnvironmentObject or a Binding. Here's an example with ObservedObject:

    import SwiftUI
    
    class Model: ObservableObject {
        @Published var loggedIn = false
    }
    
    struct ContentView: View {
        @ObservedObject var model = Model()
    
        var body: some View {
            VStack {
                if model.loggedIn {
                    HomeView().transition(.opacity)
                } else {
                    LogInView(model: model).transition(.opacity)
                }
            }
        }
    }
    
    struct HomeView: View {
        var body: some View {
            Text("Home Page")
        }
    }
    
    struct LogInView: View {
        @ObservedObject var model: Model
    
        var body: some View {
            VStack {
                Text("Welcome to Mamoot!")
                    .font(.largeTitle)
                    .fontWeight(.heavy)
                Text("We are glad to have you here.")
                Text("Please log in with your Mastodon or Twitter account to continue.")
                    .multilineTextAlignment(.center)
                    .lineLimit(4)
                    .padding()
                Spacer()
                //            FloatingTextField(title: "Username", placeholder: "Username", width: 300, type: "Username")
                //            FloatingTextField(title: "Password", placeholder: "Password", width: 300, type: "password")
                //                .padding(.top, -50)
                Spacer()
                ZStack {
                    Button(action: {
                        withAnimation(.easeInOut(duration: 1.0)) {
                            self.model.loggedIn = true
                        }
                    }) {
                        Text("Log in")
                            .foregroundColor(Color.white)
                            .bold()
                            .shadow(color: .red, radius: 10)
                            // moved modifiers here, so the whole button is tappable
                            .padding(.leading, 140)
                            .padding(.trailing, 140)
                            .padding(.top, 15)
                            .padding(.bottom, 15)
                            .background(Color.red)
                            .cornerRadius(10)
                    }
                }
                .padding(.bottom)
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-13 04:04

    The answer by @kontiki is probably the most SwiftUI-y, but I will present a different solution, probably not as good! But maybe more flexible/scalable.

    You can swap rootView of UIHostingController:

    SceneDelegate

    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
        var window: UIWindow?
        fileprivate lazy var appCoordinator: AppCoordinator = {
    
            let rootViewController: UIHostingController<AnyView> = .init(rootView: EmptyView().eraseToAny())
    
            window?.rootViewController = rootViewController
    
            let navigationHandler: (AnyScreen, TransitionAnimation) -> Void = { [unowned rootViewController, window] (newRootScreen: AnyScreen, transitionAnimation: TransitionAnimation) in
    
                UIView.transition(
                    with: window!,
                    duration: 0.5,
                    options: transitionAnimation.asUIKitTransitionAnimation,
                    animations: { rootViewController.rootView = newRootScreen },
                    completion: nil
                )
            }
    
    
            return AppCoordinator(
                dependencies: (
                    securePersistence: KeyValueStore(KeychainSwift()),
                    preferences: .default
                ),
                navigator: navigationHandler
            )
        }()
    
        func scene(
            _ scene: UIScene,
            willConnectTo session: UISceneSession,
            options connectionOptions: UIScene.ConnectionOptions
        ) {
            self.window = .fromScene(scene)
            appCoordinator.start()
        }
    }
    
    enum TransitionAnimation {
        case flipFromLeft
        case flipFromRight
    }
    
    private extension TransitionAnimation {
        var asUIKitTransitionAnimation: UIView.AnimationOptions {
            switch self {
            case .flipFromLeft: return UIView.AnimationOptions.transitionFlipFromLeft
            case .flipFromRight: return UIView.AnimationOptions.transitionFlipFromRight
            }
        }
    }
    

    AppCoordinator

    And here is the AppCoordinator:

        final class AppCoordinator {
    
        private let preferences: Preferences
        private let securePersistence: SecurePersistence
        private let navigationHandler: (AnyScreen, TransitionAnimation) -> Void
    
        init(
    
            dependencies: (securePersistence: SecurePersistence, preferences: Preferences),
    
            navigator navigationHandler: @escaping (AnyScreen, TransitionAnimation) -> Void
    
        ) {
    
            self.preferences = dependencies.preferences
            self.securePersistence = dependencies.securePersistence
            self.navigationHandler = navigationHandler
        }
    }
    
    // MARK: Internal
    internal extension AppCoordinator {
    
        func start() {
            navigate(to: initialDestination)
        }
    }
    
    // MARK: Destination
    private extension AppCoordinator {
        enum Destination {
            case welcome, getStarted, main
        }
    
        func navigate(to destination: Destination, transitionAnimation: TransitionAnimation = .flipFromLeft) {
    
            let screen = screenForDestination(destination)
            navigationHandler(screen, transitionAnimation)
        }
    
        func screenForDestination(_ destination: Destination) -> AnyScreen {
            switch destination {
            case .welcome: return AnyScreen(welcome)
            case .getStarted: return AnyScreen(getStarted)
            case .main: return AnyScreen(main)
            }
        }
    
        var initialDestination: Destination {
            guard preferences.hasAgreedToTermsAndPolicy else {
                return .welcome
            }
    
            guard securePersistence.isAccountSetup else {
                return .getStarted
            }
    
            return .main
        }
    
    }
    
    // MARK: - Screens
    private extension AppCoordinator {
    
        var welcome: some Screen {
            WelcomeScreen()
                .environmentObject(
                    WelcomeViewModel(
                        preferences: preferences,
                        termsHaveBeenAccepted: { [unowned self] in self.start() }
                    )
                )
        }
    
        var getStarted: some Screen {
            GetStartedScreen()
                .environmentObject(
                    GetStartedViewModel(
                        preferences: preferences,
                        securePersistence: securePersistence,
                        walletCreated: { [unowned self] in self.navigate(to: .main) }
                    )
            )
        }
    
        var main: some Screen {
            return MainScreen().environmentObject(
                MainViewModel(
                    preferences: preferences,
                    securePersistence: securePersistence,
                    walletDeleted: { [unowned self] in
                        self.navigate(to: .getStarted, transitionAnimation: .flipFromRight)
                    }
                )
            )
        }
    }
    
    0 讨论(0)
提交回复
热议问题