首页 > 解决方案 > SwiftUI 确定初始视图

问题描述

我试图弄清楚我在 SwiftUI 生命周期中制作的应用程序开始时的导航。根据启动时确定的设置,用户可以从三个视图之一开始。如果用户通过推送通知打开应用程序,他们应该会看到视频视图 (WrappedVideoViewController)。否则,如果他们已经登录,他们应该看到 Home 视图,如果没有,他们应该看到 Login 视图。我使用 ContentView 作为父级:

import SwiftUI

struct ContentView: View {
    @EnvironmentObject var notifService: NotificationService
    @EnvironmentObject var authState: AuthenticationState
    
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
        
    @ObservedObject var viewRouter: ViewRouter
    
    init() {
        self.viewRouter = ViewRouter.shared
    }
    
    var body: some View {
        
        print("in ContentView body")
        
        switch viewRouter.currentScreen {
        case .home:
            print("home")
            return AnyView(Home(innerGradientColor: Color.homeActiveInner, outerGradientColor: Color.homeActiveOuter))
        case .login:
            print("login")
            return
                AnyView(Login()
                .environmentObject(AuthenticationState.shared))
        case .video:
            print("video")
            return AnyView(WrappedVideoViewController())

        }
            
    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environmentObject(AuthenticationState.shared)
            .environmentObject(NotificationService())
    }
}

我的 ViewRouter 类是一个 ObservableObject,用于控制显示哪个屏幕。目前,我有这个:

//
//  NavigationState.swift
//  BumpCall-SwiftUI
//
//  Created by Ben on 1/22/21.
//

import Foundation
import SwiftUI

enum Screen {
    case home
    case video
    case login
}

class ViewRouter: ObservableObject {
    
    var authState: AuthenticationState
    var notifService: NotificationService
    
    static var shared = ViewRouter(authState: AuthenticationState.shared, notifService: NotificationService.shared)
    
    init(authState: AuthenticationState, notifService: NotificationService) {
        self.authState = authState
        self.notifService = notifService
        
        if notifService.dumbData != nil {
            currentScreen = .video
        } else if (notifService.dumbData == nil && authState.isLoggedIn()) {
            currentScreen = .home
        } else {
            currentScreen = .login
        }
        
    }
    
    @Published var currentScreen: Screen
    
}

这很棘手,因为 ViewRouter 依赖于另外两个 ObservableObjects,我在这里尝试使用 init() 来处理它们。AuthenticationState 类似乎工作正常,因为在打开应用程序时,如果您已登录,它确实会将您发送到主屏幕。但是,从通知中打开也会将我发送到主屏幕。NotificationService 更改 didReceive 响应中的 ViewRouter 单例:方法:

class NotificationService: NSObject, ObservableObject {
    @Published var dumbData: UNNotificationResponse?
    
    static var shared = NotificationService()
    
    override init() {
        super.init()
        UNUserNotificationCenter.current().delegate = self
    }
}

extension NotificationService: UNUserNotificationCenterDelegate  {
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        print("presenting notification")
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        ViewRouter.shared.notifService.dumbData = response
        print("Notification service didReceive response")
        completionHandler()
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) { }
    
}

但是单例不能更新 ContentView 以引起注意。这非常令人费解,这几天让我感到困惑,任何帮助将不胜感激。

标签: authenticationpush-notificationswiftui

解决方案


问题是它ViewRouter init()会被调用一次。正如您所提到的,当用户登录时,currentScreen将被初始化为.home. 但是,此变量之后将永远不会更改。

您基本上想要的是设置在通知委托内部currentScreen.video

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    ViewRouter.shared.currentScreen = .video //<< here set video 
    print("Notification service didReceive response")
    completionHandler()
}

推荐阅读