在 SWIFT 中收到推送通知后控制加载哪个视图控制器
Controlling which view controller loads after receiving a push notification in SWIFT
一旦我收到推送通知并滑动打开它,它只会打开我的应用程序,而不是我想要的 VC。
所以我的问题是如何加载我想要的 VC?我知道该应用程序是否打开我会将 VC 移到 didReceiveRemoteNotification
内的另一个,但如果该应用程序未打开我该怎么办?或者它是否处于后台模式?
我还有两个不同的推送通知,因此我需要它来移动两个不同 VC 之一。如何区分不同的推送通知?
谢谢。
当您 运行 申请时,您正在调用 applicationDidLaunchWithOptions:
UIApplication.sharedApplication().registerUserNotificationSettings ( UIUserNotificationSettings(forTypes: (UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound), categories: nil))
if( launchOptions != nil){
var notificationDict: AnyObject? = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey]
if(notificationDict != nil){
handleNotification(notificationDict! as! [NSObject : AnyObject])
}
}
在这里,你有 handleNotification,它基本上是我的自定义函数,我从通知中提取数据并使用该信息显示相应的控制器。
这是一个例子:
let notificationType = userInfo["aps"]!["alert"]!!["some-key-I-Need"]! as! String
var storyboard = UIStoryboard(name: "Main", bundle: nil)
let mainViewController = storyboard.instantiateInitialViewController() as! MyViewController
self.window?.rootViewController = mainViewController
除了@NickCatib 的回答之外,要了解您是否在您的应用处于 运行 时收到通知,如果是,在前台或后台您需要在 AppDelegate 中使用此方法:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
// You can determine your application state by
if UIApplication.sharedApplication().applicationState == UIApplicationState.Active {
// Do something you want when the app is active
} else {
// Do something else when your app is in the background
}
}
更新为 Swift 4.2
正如所说,您想在 applicationDidLaunchWithOptions 中注册远程通知:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let pushSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
UIApplication.shared.registerUserNotificationSettings(pushSettings)
UIApplication.shared.registerForRemoteNotifications()
}
无法知道您从 lockScreen/Background 回来后会在哪个 viewController。我所做的是从 appDelegate 发送通知。当您收到 remoteNotification 时,将调用 appDelegate 中的 didReceiveRemoteNotification。
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
let notif = JSON(userInfo) // SwiftyJSON required
根据您的通知负载,您应该首先确保它不是 nil,然后调用一个 NSNotification,它将被应该捕获此通知的 viewControllers 捕获。您也可以在这里 post 根据您收到的有效负载发送不同类型的通知。可能是这样的,举个例子吧:
if notif["callback"]["type"] != nil {
NotificationCenter.default.post(name: Notification.Name(rawValue: "myNotif"), object: nil)
// This is where you read your JSON to know what kind of notification you received
}
如果您收到消息通知并且由于令牌已过期而没有再登录,那么该通知将永远不会在视图控制器中被捕获,因为它永远不会被观看。
现在是在视图控制器中捕捉通知的部分。在 viewWillAppear:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.catchIt), name: NSNotification.Name(rawValue: "myNotif"), object: nil)
}
既然你添加了这个观察者,每次在这个控制器中调用一个通知,函数catchIt也会被调用。您必须在每个要实现特定操作的视图控制器中实现它。
func catchIt(_ userInfo: Notification){
let prefs: UserDefaults = UserDefaults.standard
prefs.removeObject(forKey: "startUpNotif")
if userInfo.userInfo?["userInfo"] != nil{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: RedirectAppInactiveVC = storyboard.instantiateViewController(withIdentifier: "RedirectAppInactiveVC") as! RedirectAppInactiveVC
self.navigationController?.pushViewController(vc, animated: true)
} else {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: RedirectAppActiveVC = storyboard.instantiateViewController(withIdentifier: "RedirectAppActiveVC") as! RedirectAppActiveVC
self.navigationController?.pushViewController(vc, animated: true)
}
}
离开视图控制器时不要忘记取消订阅通知,否则 viewController,如果仍在堆栈中,将捕获通知并执行它(你可能想要那样做,但是知道你要做什么更安全)。所以我建议在 viewWillDisappear:
中取消订阅
override func viewWillDisappear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.removeObserver(self)
}
这样做,您将加载您想要的 viewController。现在我们还没有处理所有的病例。如果您还没有打开应用程序怎么办。显然,没有加载 UIViewController,其中 none 个将能够捕获通知。您想知道您是否在 appDelegate 的 didFinishLaunchingWithOptions: 中收到通知。我做的是:
let prefs: UserDefaults = UserDefaults.standard
if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? NSDictionary {
prefs.set(remoteNotification as! [AnyHashable: Any], forKey: "startUpNotif")
prefs.synchronize()
}
现在,您已经设置了一个首选项,说明应用程序是使用远程通知启动的。在您的应用程序中应首先加载的控制器中,我建议在 viewDidAppear 中执行以下操作:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let prefs: UserDefaults = UserDefaults.standard
if prefs.value(forKey: "startUpNotif") != nil {
let userInfo: [AnyHashable: Any] = ["inactive": "inactive"]
NotificationCenter.default.post(name: Notification.Name(rawValue: "myNotif"), object: nil, userInfo: userInfo as [AnyHashable: Any])
}
}
希望对您有所帮助。我还制作了一个GitHub仓库来说明本地通知:Local Notifications Observer Pattern (similar to Remote notifications). A similar logic can be implemented using the root view Controller Local Notifications Root Pattern,我个人认为这取决于你想实现什么。
这些例子是为了说明如何简单地实现它。对于更大的项目,您最终会得到更复杂的架构,例如内部使用类似机制的协调器。
我发现以上所有答案都非常有帮助。然而,当应用程序被停用时,投票最多的人对我不起作用。后来,a 尝试结合@NickCatib 和@thefredelement 的答案,他们在执行 storyboard.instantiateInitialViewController() - "Could not cast value of type 'UINavigationController'" 时产生错误。我发现发生这种情况是因为我有一个故事板文件,其中 NavController 作为 rootViewController。为了解决这个问题,我创建了一个新的导航控制器,它解决了一个问题:我丢失了应用程序的正确导航,并且视图甚至没有显示后退按钮。我的问题的答案是使用@NickCatib 和@thefredelement 答案,但是使用标识符实例化视图并推送它,使用 rootViewController 作为 UINavigationController,如下所示。
let rootViewController = self.window?.rootViewController as! UINavigationController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let mvc = storyboard.instantiateViewControllerWithIdentifier("MyViewController") as!
MyViewController
rootViewController.pushViewController(mvc, animated: true)
这对我来说效果很好,我没有丢失应用程序的正确导航属性。
对先前答案的补充信息。
根据你的状态,你可以改变你的逻辑。
在方法 didReceiveRemoteNotification 中:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {}
你可以这样切换
let state = UIApplication.sharedApplication().applicationState
switch state {
case UIApplicationState.Active:
case UIApplicationState.Inactive:
case UIApplicationState.Background:
}
根据应用的当前状态执行您想要的操作。
Swift兔子回答最好
我要补充的是,当应用程序从关闭状态变为活动状态时,它仍然缺失。
你可以在AppDelegate里面添加,didFinishedLaunchingWithOptions:
if let notification = launchOptions?[.remoteNotification] as? [AnyHashable : Any] {
notificationsUserInfo = notification as [AnyHashable : Any]
serveNotifications = true
}
您可以使用通知的值创建全局变量或 userdefault,并使用标志让应用程序的其余部分知道有通知。
一旦 mainViewController 可见,您就可以执行操作来处理通知。
override func viewDidAppear(_ animated: Bool) {
if serveNotifications {
notificationManager.sharedInstance.processNotification(userInfo: notificationsUserInfo)
serveNotifications = false
}
}
保重。
一旦我收到推送通知并滑动打开它,它只会打开我的应用程序,而不是我想要的 VC。
所以我的问题是如何加载我想要的 VC?我知道该应用程序是否打开我会将 VC 移到 didReceiveRemoteNotification
内的另一个,但如果该应用程序未打开我该怎么办?或者它是否处于后台模式?
我还有两个不同的推送通知,因此我需要它来移动两个不同 VC 之一。如何区分不同的推送通知?
谢谢。
当您 运行 申请时,您正在调用 applicationDidLaunchWithOptions:
UIApplication.sharedApplication().registerUserNotificationSettings ( UIUserNotificationSettings(forTypes: (UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound), categories: nil))
if( launchOptions != nil){
var notificationDict: AnyObject? = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey]
if(notificationDict != nil){
handleNotification(notificationDict! as! [NSObject : AnyObject])
}
}
在这里,你有 handleNotification,它基本上是我的自定义函数,我从通知中提取数据并使用该信息显示相应的控制器。
这是一个例子:
let notificationType = userInfo["aps"]!["alert"]!!["some-key-I-Need"]! as! String
var storyboard = UIStoryboard(name: "Main", bundle: nil)
let mainViewController = storyboard.instantiateInitialViewController() as! MyViewController
self.window?.rootViewController = mainViewController
除了@NickCatib 的回答之外,要了解您是否在您的应用处于 运行 时收到通知,如果是,在前台或后台您需要在 AppDelegate 中使用此方法:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
// You can determine your application state by
if UIApplication.sharedApplication().applicationState == UIApplicationState.Active {
// Do something you want when the app is active
} else {
// Do something else when your app is in the background
}
}
更新为 Swift 4.2
正如所说,您想在 applicationDidLaunchWithOptions 中注册远程通知:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let pushSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
UIApplication.shared.registerUserNotificationSettings(pushSettings)
UIApplication.shared.registerForRemoteNotifications()
}
无法知道您从 lockScreen/Background 回来后会在哪个 viewController。我所做的是从 appDelegate 发送通知。当您收到 remoteNotification 时,将调用 appDelegate 中的 didReceiveRemoteNotification。
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
let notif = JSON(userInfo) // SwiftyJSON required
根据您的通知负载,您应该首先确保它不是 nil,然后调用一个 NSNotification,它将被应该捕获此通知的 viewControllers 捕获。您也可以在这里 post 根据您收到的有效负载发送不同类型的通知。可能是这样的,举个例子吧:
if notif["callback"]["type"] != nil {
NotificationCenter.default.post(name: Notification.Name(rawValue: "myNotif"), object: nil)
// This is where you read your JSON to know what kind of notification you received
}
如果您收到消息通知并且由于令牌已过期而没有再登录,那么该通知将永远不会在视图控制器中被捕获,因为它永远不会被观看。
现在是在视图控制器中捕捉通知的部分。在 viewWillAppear:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.catchIt), name: NSNotification.Name(rawValue: "myNotif"), object: nil)
}
既然你添加了这个观察者,每次在这个控制器中调用一个通知,函数catchIt也会被调用。您必须在每个要实现特定操作的视图控制器中实现它。
func catchIt(_ userInfo: Notification){
let prefs: UserDefaults = UserDefaults.standard
prefs.removeObject(forKey: "startUpNotif")
if userInfo.userInfo?["userInfo"] != nil{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: RedirectAppInactiveVC = storyboard.instantiateViewController(withIdentifier: "RedirectAppInactiveVC") as! RedirectAppInactiveVC
self.navigationController?.pushViewController(vc, animated: true)
} else {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: RedirectAppActiveVC = storyboard.instantiateViewController(withIdentifier: "RedirectAppActiveVC") as! RedirectAppActiveVC
self.navigationController?.pushViewController(vc, animated: true)
}
}
离开视图控制器时不要忘记取消订阅通知,否则 viewController,如果仍在堆栈中,将捕获通知并执行它(你可能想要那样做,但是知道你要做什么更安全)。所以我建议在 viewWillDisappear:
中取消订阅override func viewWillDisappear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.removeObserver(self)
}
这样做,您将加载您想要的 viewController。现在我们还没有处理所有的病例。如果您还没有打开应用程序怎么办。显然,没有加载 UIViewController,其中 none 个将能够捕获通知。您想知道您是否在 appDelegate 的 didFinishLaunchingWithOptions: 中收到通知。我做的是:
let prefs: UserDefaults = UserDefaults.standard
if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? NSDictionary {
prefs.set(remoteNotification as! [AnyHashable: Any], forKey: "startUpNotif")
prefs.synchronize()
}
现在,您已经设置了一个首选项,说明应用程序是使用远程通知启动的。在您的应用程序中应首先加载的控制器中,我建议在 viewDidAppear 中执行以下操作:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let prefs: UserDefaults = UserDefaults.standard
if prefs.value(forKey: "startUpNotif") != nil {
let userInfo: [AnyHashable: Any] = ["inactive": "inactive"]
NotificationCenter.default.post(name: Notification.Name(rawValue: "myNotif"), object: nil, userInfo: userInfo as [AnyHashable: Any])
}
}
希望对您有所帮助。我还制作了一个GitHub仓库来说明本地通知:Local Notifications Observer Pattern (similar to Remote notifications). A similar logic can be implemented using the root view Controller Local Notifications Root Pattern,我个人认为这取决于你想实现什么。
这些例子是为了说明如何简单地实现它。对于更大的项目,您最终会得到更复杂的架构,例如内部使用类似机制的协调器。
我发现以上所有答案都非常有帮助。然而,当应用程序被停用时,投票最多的人对我不起作用。后来,a 尝试结合@NickCatib 和@thefredelement 的答案,他们在执行 storyboard.instantiateInitialViewController() - "Could not cast value of type 'UINavigationController'" 时产生错误。我发现发生这种情况是因为我有一个故事板文件,其中 NavController 作为 rootViewController。为了解决这个问题,我创建了一个新的导航控制器,它解决了一个问题:我丢失了应用程序的正确导航,并且视图甚至没有显示后退按钮。我的问题的答案是使用@NickCatib 和@thefredelement 答案,但是使用标识符实例化视图并推送它,使用 rootViewController 作为 UINavigationController,如下所示。
let rootViewController = self.window?.rootViewController as! UINavigationController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let mvc = storyboard.instantiateViewControllerWithIdentifier("MyViewController") as!
MyViewController
rootViewController.pushViewController(mvc, animated: true)
这对我来说效果很好,我没有丢失应用程序的正确导航属性。
对先前答案的补充信息。
根据你的状态,你可以改变你的逻辑。 在方法 didReceiveRemoteNotification 中:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {}
你可以这样切换
let state = UIApplication.sharedApplication().applicationState
switch state {
case UIApplicationState.Active:
case UIApplicationState.Inactive:
case UIApplicationState.Background:
}
根据应用的当前状态执行您想要的操作。
Swift兔子回答最好
我要补充的是,当应用程序从关闭状态变为活动状态时,它仍然缺失。
你可以在AppDelegate里面添加,didFinishedLaunchingWithOptions:
if let notification = launchOptions?[.remoteNotification] as? [AnyHashable : Any] {
notificationsUserInfo = notification as [AnyHashable : Any]
serveNotifications = true
}
您可以使用通知的值创建全局变量或 userdefault,并使用标志让应用程序的其余部分知道有通知。
一旦 mainViewController 可见,您就可以执行操作来处理通知。
override func viewDidAppear(_ animated: Bool) {
if serveNotifications {
notificationManager.sharedInstance.processNotification(userInfo: notificationsUserInfo)
serveNotifications = false
}
}
保重。