Mein Problem ist, dass ich einen Ladebildschirm für die erste Push-Benachrichtigungsaufforderung anzeigen möchte. "Die App möchte Ihnen Push-Benachrichtigungen senden."
Wenn der Benutzer also yes
drückt, kann ich fortfahren und die App in den dann aufgerufenen Delegatmethoden starten:
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
[self hideLoadingScreen];
}
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
[self hideLoadingScreen];
}
Wenn der Benutzer jedoch no
drückt, wird keine dieser Methoden aufgerufen, was sinnvoll ist. Meine Frage ist, gibt es eine andere Delegat-Methode, die ausgelöst wird, wenn er ablehnt?
Mein Problem ist, wenn no
ausgewählt ist, verschwinden die Ladebildschirme nie. Also muss ich irgendwie wissen, wann der Benutzer mit der Auswahl fertig ist.
Wenn in iOS 7 die Push-Benachrichtigungsaufforderung des Systems angezeigt wird, wird die App inaktiv und UIApplicationWillResignActiveNotification wird ausgelöst. Wenn der Benutzer auf die Aufforderung antwortet (entweder Ja oder Nein), wird die App wieder aktiv und UIApplicationDidBecomeActiveNotification wird ausgelöst.
Sie können also auf diese Benachrichtigung warten und dann den Ladebildschirm ausblenden.
Hinweis: Während die Eingabeaufforderung angezeigt wird, sind die Startschaltflächen, das Benachrichtigungscenter und das Kontrollcenter deaktiviert, sodass keine falsch positiven UIApplicationDidBecomeActiveNotification ausgelöst werden kann. Wenn der Benutzer jedoch die Sperrtaste drückt, wird UIApplicationDidBecomeActiveNotification ausgelöst.
Sie können immer aktuelle zulässige Benachrichtigungstypen erhalten von:
UIRemoteNotificationType notificationTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
Beachten Sie, dass der Benutzer die Benachrichtigung auch in den Telefoneinstellungen deaktivieren kann.
Wenn Sie dies unter didRegisterForRemoteNotificationsWithDeviceToken überprüfen, sollten Sie sehen, ob die von Ihnen gewünschten Typen aktiviert sind.
So habe ich es in Swift 3 gemacht. Der Schlüssel ist hier, den Lebenszyklusstatus der Anwendung intern zu verfolgen. Wenn die Push-Eingabeaufforderung angezeigt wird, tritt die Anwendung als aktiv zurück, tritt jedoch nicht in den Hintergrund ein. Dies ist alles in meiner AppDelegate.Swift.
Dies ist ein wirklich großer Hack und wird in der Produktion nicht empfohlen. Apple könnte die Art und Weise ändern, in der diese Benachrichtigungen angezeigt werden, und dies kann jederzeit unterbrochen werden. Dies wurde mit verschiedenen iPhones und iPads mit iOS 9 und 10 getestet.
/// An internal value used to track application lifecycle state
enum ApplicationLifecycleState {
case willResignActive
case didEnterBackground
case willEnterForeground
case didBecomeActive
case unknown
}
/// This is used purely for tracking the application lifecycle for handling the system Push notification alert
var internalLifecycleState: ApplicationLifecycleState = .unknown {
didSet {
// If we're not in the middle of asking for Push permissions, none of the below applies, just bail out here
if !isAskingForPushPermissions { return }
// WARNING: Application lifecycle trickery ahead
// The normal application lifecycle calls for backgrounding are as follows:
// applicationWillResignActive -> applicationDidEnterBackground -> applicationWillEnterForeground -> applicationDidBecomeActive
// However, when the system Push notification alert is presented, the application resigns active, but does not enter the background:
// applicationWillResignActive -> [user taps on alert] -> applicationDidBecomeActive
// We can use this discrepancy to our advantage to detect if the user did not allow Push permissions
// If applicationDidBecomeActive
// AND the previous state was applicationWillResignActive
// AND the notification types bitmask is 0, we know that the user did not allow Push permissions
// User denied permissions
if internalLifecycleState == .didBecomeActive
&& oldValue == .willResignActive
&& UIApplication.shared.currentUserNotificationSettings?.types.rawValue == 0 {
// We're done
firePushCompletionBlockAndCleanup(registered: false)
} else {
// The state below can only be entered on iOS 10 devices.
// If the user backgrounds the app while the system alert is being shown,
// when the app is foregrounded the alert will dismiss itself without user interaction.
// This is the equivalent of the user denying Push permissions.
// On iOS versions below 10, the user cannot background the app while a system alert is being shown.
if #available(iOS 10, *), internalLifecycleState == .didBecomeActive {
firePushCompletionBlockAndCleanup(registered: false)
}
}
}
}
/// Used internally to track if the system Push notification alert is currently being presented
var isAskingForPushPermissions = false
typealias PushNotificationRegistrationCompletionBlock = ((_ registered: Bool) -> Void)
// ...
func applicationWillResignActive(_ application: UIApplication) {
internalLifecycleState = .willResignActive
}
func applicationDidEnterBackground(_ application: UIApplication) {
internalLifecycleState = .didEnterBackground
}
func applicationWillEnterForeground(_ application: UIApplication) {
internalLifecycleState = .willEnterForeground
}
func applicationDidBecomeActive(_ application: UIApplication) {
internalLifecycleState = .didBecomeActive
}
// ...
func setupPushNotifications(_ application: UIApplication = UIApplication.shared, completion: @escaping PushNotificationRegistrationCompletionBlock) {
isAskingForPushPermissions = true
pushCompletionBlock = completion
let settings = UIUserNotificationSettings(types: [.alert, .sound, .badge], categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
}
fileprivate func firePushCompletionBlockAndCleanup(registered: Bool) {
pushCompletionBlock?(registered)
pushCompletionBlock = nil
isAskingForPushPermissions = false
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// application:didRegisterForRemoteNotificationsWithDeviceToken may be called more than once (once for each notification type)
// By checking that the notification types bitmask is greater than 0, we can find the final time this is called (after the user actually tapped "allow")
// If the user denied Push permissions, this function is never called with a positive notification type bitmask value
if UIApplication.shared.currentUserNotificationSettings?.types.rawValue ?? 0 > 0 {
firePushCompletionBlockAndCleanup(registered: true)
}
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register for notifications with error: " + error.localizedDescription)
firePushCompletionBlockAndCleanup(registered: false)
}
Verwendungszweck:
appDelegate.setupPushNotifications(completion: { [weak self] (registered) in
// If registered is false, the user denied permissions
})
Für Swift 3 und Swift 4.0 Verwenden von NotificationCenter und der AppDelegate-Methode didRegister notificationSettings
. NotificationSettings zeigt an, ob sich die Benutzer für Badges, Sounds usw. entschieden haben, und ist ein leeres Array, wenn sie Push-Benachrichtigungen ablehnen. Es wird speziell ausgelöst, wenn Benutzer auf die Eingabeaufforderung für Push-Benachrichtigungen antworten, und scheint das zu sein, was die meisten Entwickler verwenden, da dies spezifischer ist als die Überprüfung von didBecomeActive. Aber Apple könnte das ändern. Wer weiß?
Leider verfügt NotificationCenter nicht über einen voreingestellten Benachrichtigungsnamen. Sie müssen also entweder eine Einrichtung und eine Erweiterung einrichten (siehe Ende) oder den Rohwert in verwenden (dazu hat SO mehr).
In AppDelegate:
func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
// if not registered users will have an empty set of settings
let accepted: Bool = !notificationSettings.types.isEmpty
NotificationCenter.default.post(name: Notification.Name(rawValue: "didRespondToPrompt"), object: self, userInfo: ["didAccept" : accepted])
}
Dann beobachten Sie, wo immer Sie möchten, zum Beispiel in einem View-Controller:
class MyViewController: UIViewController {
//MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(MyViewController.didRespondToPushPrompt(_:)), name: NSNotification.Name(rawValue: "didRespondToPrompt"), object: nil)
}
@objc func didRespondToPushPrompt(_ notification: Notification) {
if let userInfo: [AnyHashable : Any] = notification.userInfo, let didAccept: Bool = userInfo[NSNotificationKeyNames.didAccept] as? Bool, !didAccept {
//if user doesn't accept, do this...
} else {
//all other situations code goes here
}
}
}
Ein paar Dinge: Erstens verwende ich für Swift 4.0 "@objc" vor einer Methode, aber für Swift 3 ist dies nicht erforderlich.
Für die Verwendung von NotificationCenter habe ich in der Praxis "rawValue" nicht verwendet. Stattdessen habe ich eine Erweiterung so gemacht:
import Foundation
extension NSNotification.Name {
static let DidRegisterForPushNotifications = NSNotification.Name("DidRegisterForPushNotifications")
}
Was könnte ich dann so gebrauchen:
NotificationCenter.default.post(name: Notification.Name.DidRegisterForPushNotifications, object: self, userInfo: ["didAccept" : myBool])
Usw. usw.
Könnten Sie nicht einfach Folgendes tun:
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
BOOL pushEnabled = notificationSettings.types & UIUserNotificationTypeAlert;
}
Diese Methode sollte der Rückruf für die Eingabeaufforderung für Push-Benachrichtigungen sein. Von dort aus können Sie die Bitmaske überprüfen, um zu sehen, ob Push-Benachrichtigungen aktiviert waren oder nicht.
Einige der Antworten sind nicht mehr relevant oder komplizierter als sie sein sollten, da das UserNotifications Framework und iOS 10 diese Daten leicht erhalten können:
let center = UNUserNotificationCenter.current()
// Request permission to display alerts and play sounds.
center.requestAuthorization(options: [.alert, .sound])
{ (granted, error) in
// Enable or disable features based on authorization.
}
Ich schätze, Sie können eine BOOL-Variable haben, um sie in Ihrer AppDelegate zu überprüfen, da es scheinbar keine andere Möglichkeit gibt als die Verwendung externer APIs. Siehe this .
AppDelegate.m
// declare a BOOL
BOOL allow = NO;
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
allow = YES;
[self hideLoadingScreen];
}
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
allow = YES;
[self hiedLoadingScreen];
}
Nun, ich vermute, Sie können auf diese BOOL-Variable zugreifen, um zu unterscheiden, ob Sie nicht zulassen drücken oder nicht.
Hier ist ein Swift 2 Codebeispiel für Sie ... Es ist etwas kompliziert, aber ich hoffe, meine Kommentare werden Ihnen helfen, es zu verstehen.
Variablen definieren
var appDidBecomeActiveCount = 0
var userDefaults:NSUserDefaults!
AppDelegate - didFinishLaunchingWithOptions
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
userDefaults = NSUserDefaults.standardUserDefaults()
if userDefaults.valueForKey("FirstLaunche") == nil {
userDefaults.setBool(true, forKey: "FirstLaunche")
userDefaults.synchronize()
}
// Register for notification
//iOS 8+
let settings:UIUserNotificationSettings = UIUserNotificationSettings(forTypes: [UIUserNotificationType.Alert , UIUserNotificationType.Badge ,UIUserNotificationType.Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
UIApplication.sharedApplication().registerForRemoteNotifications()
}
AppDelegate - applicationDidBecomeActive
func applicationDidBecomeActive(application: UIApplication) {
//Delay until alert get dismissed and notification type setted in app
delay(0.5, closure: { () -> () in
self.checkTheDilemma()
})
}
//I love this short method <3_<3
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
Aktion prüfen
func checkTheDilemma (){
//Checking if this user turned off Push notifications or didn't allow it at all
let notificationType = UIApplication.sharedApplication().currentUserNotificationSettings()?.types
if userDefaults.valueForKey("FirstLaunche") as! Bool == true {
//User now is asked for notification permission because it's app's first launche
// if appDidBecomeActiveCount == 0 --> Pop up message will appeare
// if appDidBecomeActiveCount == 1 --> Pop up message dismissed
// if notificationType?.rawValue == 0 --> Notifications off
// if notificationType?.rawValue > 0 --> Notifications on
if notificationType?.rawValue == 0
&& appDidBecomeActiveCount == 1 { //If user disabled notifications from pop up alert
// ** User just tapped "Don't allow" btn :\
// Do what ever you are here for
//Now set FirstLaunche = false
userDefaults.setBool(false, forKey: "FirstLaunche")
userDefaults.synchronize()
}
} else {
if notificationType?.rawValue == 0
&& appDidBecomeActiveCount == 0 { // This guy is not registered for Push notification
// ** User disabled notifications in past (because this is not his first launch)
}
}
appDidBecomeActiveCount++
}
Sie können feststellen, ob der Benutzer die Benachrichtigungsaufforderung in der didRegisterUserNotificationSettings
-Methode, die nach dem Aufruf von registerForRemoteNotificationTypes
ausgelöst wird, abgebrochen hat, indem Sie den notificationSettings.types
überprüfen.
Wenn Sie eine Reihe von Einstellungen angefordert haben, aber notificationSettings.types == UIUserNotificationTypeNone
bedeutet, hat der Benutzer die Eingabeaufforderung abgebrochen.
Vergessen Sie nicht, dass die registerForRemoteNotificationTypes
-Methode jetzt veraltet ist!
2. Mai 2019
Dies ist die Implementierung, um zu überprüfen, ob Benachrichtigungen jederzeit in Ihrer App autorisiert sind. Rufen Sie einfach diese Funktion auf.
private func checkNotificationsAuthorizationStatus() {
let userNotificationCenter = UNUserNotificationCenter.current()
userNotificationCenter.getNotificationSettings { (notificationSettings) in
switch notificationSettings.authorizationStatus {
case .authorized:
print("The app is authorized to schedule or receive notifications.")
case .denied:
print("The app isn't authorized to schedule or receive notifications.")
case .notDetermined:
print("The user hasn't yet made a choice about whether the app is allowed to schedule notifications.")
case .provisional:
print("The application is provisionally authorized to post noninterruptive user notifications.")
}
}
}