SwiftUI - 当用户打开推送通知时打开特定视图 [英] SwiftUI - Open a specific View when user opens a Push Notification

查看:118
本文介绍了SwiftUI - 当用户打开推送通知时打开特定视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

这是我到目前为止的代码:

class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) ->布尔{//解析初始化...//通知registerForPushNotifications()//通知徽章UIApplication.shared.applicationIconBadgeNumber = 0//当应用程序在前台时启动通知UNUserNotificationCenter.current().delegate = self返回真}//此函数将在用户点击通知后立即调用func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping() -> Void) {打印(从推送通知点击打开的应用程序")UIApplication.shared.applicationIconBadgeNumber = 0完成处理程序()}}@主要的结构我的应用程序:应用程序{@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegatevar主体:一些场景{窗口组{内容视图(当前标签:Tab.home)}}}

该应用程序打印从 PushNotification tap 中打开的应用程序",但是如果我在 AppDelegate 中放置一个变量并且我使用 .onReceive 或 .onChange 监听 Co​​ntentView 中该变量的更改,则不会发生任何事情

struct ContentView: 查看 {@ObservedObject var appState = AppState()@State var currentTab : Tab@State var noReloadAddItemView = falsevar主体:一些视图{TabView(选择:$appState.currentTab){导航视图{HomeView(appState: appState, noReloadAddItemView: $noReloadAddItemView)}.tabItem {如果 appState.currentTab == .home {图像(系统名称:house.fill")} 别的 {图像(系统名称:房子")}文本(LocalizedStringKey(HomeTabMenu"))}.tag(Tab.home)导航视图{SearchView(appState: appState, noReloadAddItemView: $noReloadAddItemView)}.tabItem {如果 appState.currentTab == .search {图像(系统名称:放大镜.circle.fill")} 别的 {图像(系统名称:放大镜")}文本(LocalizedStringKey(SearchTabMenu"))}.tag(Tab.search)导航视图{AddItemView(appState: appState, noReloadAddItemView: $noReloadAddItemView)}.tabItem {如果 appState.currentTab == .add {图像(系统名称:plus.circle.fill")} 别的 {图像(系统名称:plus.circle")}文本(LocalizedStringKey(SellTabMenu"))}.tag(Tab.add)导航视图{ShoppingCartFavoritesView(appState: appState, noReloadAddItemView: $noReloadAddItemView)}.tabItem {如果 appState.currentTab == .favorites {图像(系统名称:cart.fill")} 别的 {图像(系统名称:购物车")}文本(LocalizedStringKey(CartTabMenu"))}.tag(标签收藏夹)导航视图{ProfileView(appState: appState, noReloadAddItemView: $noReloadAddItemView)}.tabItem {如果 appState.currentTab == .profile {图像(系统名称:person.fill")} 别的 {图像(系统名称:人")}文本(LocalizedStringKey(ProfileTabMenu"))}.tag(Tab.profile)}.accentColor(颜色(ColorMainDark"))}}struct ContentView_Previews: PreviewProvider {静态 var 预览:一些视图 {内容视图(当前标签:Tab.home)}}类 AppState:ObservableObject {@Published var currentTab : Tab = .home}枚举选项卡{案例主页,搜索,添加,收藏夹,个人资料}

解决方案

您需要某种共享状态,您可以修改 SwiftUI 知道要对其做出反应的某种共享状态.ObservableObject 非常适合:

class AppState: ObservableObject {静态让共享 = AppState()@Published var pageToNavigationTo :字符串?}

然后,要聆听并回应,您可以在主视图中执行几种不同的方法.

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

struct ContentView : 查看 {@ObservedObject var appState = AppState.shared//<-- 注意这一点@State var 导航 = falsevar pushNavigationBinding : Binding{.init { () ->布尔输入appState.pageToNavigationTo != nil} set: { (newValue) inif !newValue { appState.pageToNavigationTo = nil }}}var主体:一些视图{导航视图{文本(我的内容").overlay(NavigationLink(destination: Dest(message: appState.pageToNavigationTo ?? ""),isActive: pushNavigationBinding) {空视图()})}}}结构目标:查看{变量消息:字符串var主体:一些视图{文本(\(消息)")}}

或者,您可以使用 onReceive:

struct ContentView : 查看 {@ObservedObject var appState = AppState.shared@State var 导航 = falsevar主体:一些视图{导航视图{虚拟堆栈{如果导航{NavigationLink(destination: Text(Test"), isActive: $navigate ) {空视图()}}文本(我的内容").onReceive(appState.$pageToNavigationTo) { (nav) in如果导航 != nil { 导航 = 真 }}}}}}

我会将您的特定 NavigationView、NavigationLink、TabView 等的实现细节留给您,但这应该会让您开始.

最后,一个功能齐全的最小示例,模拟通知并显示导航视图:

<预><代码>类 AppDelegate:NSObject、UIApplicationDelegate、UNUserNotificationCenterDelegate {func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) ->布尔{DispatchQueue.main.asyncAfter(deadline: .now() + 2) {打印(调度")AppState.shared.pageToNavigationTo = "test";}返回真}}类 AppState:ObservableObject {静态让共享 = AppState()@Published var pageToNavigationTo :字符串?}@主要的struct MultiWindowApp: App {@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegatevar主体:一些场景{窗口组{内容视图()}}}结构内容视图:查看{@ObservedObject var appState = AppState.shared@State var 导航 = falsevar pushNavigationBinding : Binding{.init { () ->布尔输入appState.pageToNavigationTo != nil} set: { (newValue) inif !newValue { appState.pageToNavigationTo = nil }}}var主体:一些视图{导航视图{文本(我的内容").overlay(NavigationLink(destination: Dest(message: appState.pageToNavigationTo ?? ""),isActive: pushNavigationBinding) {空视图()})}}}结构目标:查看{变量消息:字符串var主体:一些视图{文本(\(消息)")}}

A have an app made in SwiftUI, with Parse used for DB. I'm some parts of the app i've integrated some cloud functions that send notifications (for example: when someone send's you a message, you will receive a push notification triggered by that cloud function). In the past days i'm struggling and searching for how to open a specific view when you press the Notification to open the app. I've found some solutions, but could not make them work.

This is the code that i have so far :

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)
        }
    }
}

The app prints "app opened from PushNotification tap", but if I put a variable in AppDelegate and I listen for changes in ContentView with .onReceive or .onChange for that variable, nothing is hapenning

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
}

解决方案

You need some sort of shared state that you can modify that SwiftUI knows to react to. An ObservableObject is perfect for this:

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

Then, to listen to it and respond to it, you can do a couple different methods in your main view.

Option 1 -- NavigationLink binding based on the value of the 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)")
    }
}

Or, you could use 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 }
                    }
            }
        }
    }
}

I'll leave the implementation details of your specific NavigationView, NavigationLink, TabView, etc to you, but this should get you started.

Finally, a fully-functional minimal example that mocks a notification and shows how the navigation view:


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)")
    }
}

这篇关于SwiftUI - 当用户打开推送通知时打开特定视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆