SwiftUI - 当用户打开推送通知时打开特定视图

14

我有一个使用SwiftUI制作的应用程序,并使用Parse作为数据库。在应用程序的某些部分中,我集成了一些云功能,以发送通知(例如:当有人向您发送消息时,您将收到由该云功能触发的推送通知)。在过去的几天里,我一直在苦苦寻找如何在按下通知以打开应用程序时打开特定视图的方法。我已经找到了一些解决方案,但无法使它们起作用。

这是我目前所拥有的代码:

class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        
        //Parse Intialization
        ...
        //notifications
        registerForPushNotifications()
        //Notification Badge
        UIApplication.shared.applicationIconBadgeNumber = 0
        // start notification while app is in Foreground
        UNUserNotificationCenter.current().delegate = self
        return true
    }
    
    // This function will be called right after user tap on the notification
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        print("app opened from PushNotification tap")
        UIApplication.shared.applicationIconBadgeNumber = 0
      completionHandler()
    }
}

@main
struct MyApp: App {
    
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView(currentTab: Tab.home)
        }
    }
}

该应用程序会打印“从 PushNotification 点击打开的应用程序”,但如果我在AppDelegate中放置一个变量,并使用.onReceive或.onChange来监听ContentView中该变量的更改,什么也不会发生。
struct ContentView: View {
    @ObservedObject var appState = AppState()
    @State var currentTab : Tab
    @State var noReloadAddItemView = false
    var body: some View {
        TabView(selection: $appState.currentTab) {
            
            NavigationView {
                HomeView(appState: appState, noReloadAddItemView: $noReloadAddItemView)
                
            }
            .tabItem {
                if appState.currentTab == .home {
                    Image(systemName: "house.fill")
                } else {
                    Image(systemName: "house")
                }
                
                Text(LocalizedStringKey("HomeTabMenu"))
                
            }.tag(Tab.home)
            
            NavigationView {
                SearchView(appState: appState, noReloadAddItemView: $noReloadAddItemView)
            }
            .tabItem {
                if appState.currentTab == .search {
                    Image(systemName: "magnifyingglass.circle.fill")
                } else {
                    Image(systemName: "magnifyingglass")
                }
                Text(LocalizedStringKey("SearchTabMenu"))
            }.tag(Tab.search)
            
            NavigationView {
                AddItemView(appState: appState, noReloadAddItemView: $noReloadAddItemView)
            }
            .tabItem {
                if appState.currentTab == .add {
                    Image(systemName: "plus.circle.fill")
                } else {
                    Image(systemName: "plus.circle")
                }
                Text(LocalizedStringKey("SellTabMenu"))
            }.tag(Tab.add)
            
            NavigationView {
                ShoppingCartFavoritesView(appState: appState, noReloadAddItemView: $noReloadAddItemView)
            }
            .tabItem {
                if appState.currentTab == .favorites {
                    Image(systemName: "cart.fill")
                } else {
                    Image(systemName: "cart")
                }
                Text(LocalizedStringKey("CartTabMenu"))
            }.tag(Tab.favorites)
            
            NavigationView {
                ProfileView(appState: appState, noReloadAddItemView: $noReloadAddItemView)
            }
            .tabItem {
                if appState.currentTab == .profile {
                    Image(systemName: "person.fill")
                } else {
                    Image(systemName: "person")
                }
                Text(LocalizedStringKey("ProfileTabMenu"))
            }.tag(Tab.profile)
        }
        .accentColor(Color("ColorMainDark"))
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(currentTab: Tab.home)
    }
}

class AppState: ObservableObject {
    @Published var currentTab : Tab = .home
}

enum Tab {
    case home, search, add, favorites, profile
}
1个回答

20

您需要一种可以修改的共享状态,SwiftUI知道要对其进行反应。 ObservableObject非常适合此目的:

class AppState: ObservableObject {
    static let shared = AppState()
    @Published var pageToNavigationTo : String?
}

然后,为了听取并响应它,您可以在主视图中使用几种不同的方法。

选项1-- NavigationLink绑定基于ObservedObject的值:

struct ContentView : View {
    @ObservedObject var appState = AppState.shared //<-- note this
    @State var navigate = false
    
    var pushNavigationBinding : Binding<Bool> {
        .init { () -> Bool in
            appState.pageToNavigationTo != nil
        } set: { (newValue) in
            if !newValue { appState.pageToNavigationTo = nil }
        }
    }
    
    var body: some View {
        NavigationView {
            Text("My content")
                .overlay(NavigationLink(destination: Dest(message: appState.pageToNavigationTo ?? ""),
                                        isActive: pushNavigationBinding) {
                    EmptyView()
                })
        }
    }
}

struct Dest : View {
    var message : String
    var body: some View {
        Text("\(message)")
    }
}

或者,你可以使用onReceive

struct ContentView : View {
    @ObservedObject var appState = AppState.shared
    @State var navigate = false
    
    var body: some View {
        NavigationView {
            VStack {
                if navigate {
                    NavigationLink(destination: Text("Test"), isActive: $navigate ) {
                        EmptyView()
                    }
                }
                Text("My content")
                    .onReceive(appState.$pageToNavigationTo) { (nav) in
                        if nav != nil { navigate = true }
                    }
            }
        }
    }
}

我将把你特定的NavigationView、NavigationLink、TabView等实现细节留给你,但这应该可以帮助你入门。

最后,这是一个完全功能的最小示例,模拟通知并展示了导航视图的方法:


class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
     
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            print("Dispatch")
            AppState.shared.pageToNavigationTo = "test"
        }
        
        return true
    }
}

class AppState: ObservableObject {
    static let shared = AppState()
    @Published var pageToNavigationTo : String?
}

@main
struct MultiWindowApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView : View {
    @ObservedObject var appState = AppState.shared
    @State var navigate = false
    
    var pushNavigationBinding : Binding<Bool> {
        .init { () -> Bool in
            appState.pageToNavigationTo != nil
        } set: { (newValue) in
            if !newValue { appState.pageToNavigationTo = nil }
        }
    }
    
    var body: some View {
        NavigationView {
            Text("My content")
                .overlay(NavigationLink(destination: Dest(message: appState.pageToNavigationTo ?? ""),
                                        isActive: pushNavigationBinding) {
                    EmptyView()
                })
        }
    }
}


struct Dest : View {
    var message : String
    var body: some View {
        Text("\(message)")
    }
}

1
我之前不知道在Swift中可以有一个Observable单例...但我猜这是可行的。 - John Sorensen
1
你也可以在应用程序委托和视图之间显式传递引用,但这可能需要使用可选项。 - jnpdx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接