本地推送通知

iOS常用通知

1、本地推送通知:(Local Notification):
??不需要聯(lián)網(wǎng)就可以發(fā)出的通知, 常用于提醒用戶完成一些任務(wù)锹杈,如:鬧鐘撵孤,日歷待辦事項(xiàng)提醒,備注等等;
2竭望、遠(yuǎn)程推送通知:(Remote Notification)
??從遠(yuǎn)程服務(wù)器(APNs)推送給客戶端的通知(需要聯(lián)網(wǎng)), 模擬器無(wú)法調(diào)試遠(yuǎn)程推送;
??常用于解決獲取傳統(tǒng)數(shù)據(jù)的局限性邪码,能讓數(shù)據(jù)實(shí)時(shí)更新, 在聯(lián)網(wǎng)的情況下, 蘋果設(shè)備都會(huì)與APNs建立長(zhǎng)連接, 因此能保證數(shù)據(jù)傳輸速度快及保證數(shù)據(jù)的實(shí)時(shí)性...

本篇只介紹本地推送通知:

通知的授權(quán)與注冊(cè): (分iOS10.0 以后 與 之前)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 通知的授權(quán)檢查
    [self checkNotificationAuthorization];
        
    // 本地通知 + App未存活 (iOS 10.0以前)
    UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    if (notification.userInfo) {
        //  處理收到的通知
    }
    return YES;
}

/**
 檢查本地通知的授權(quán)
 */
- (void)checkNotificationAuthorization {
    if (@available(iOS 10.0, *)) {
        UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
        // 這兩個(gè)必須要寫(xiě)在上面  每次程序重新打開(kāi)時(shí)都得設(shè)置一遍...
        center.delegate = self;
        //添加category: iOS 10.0以后
        [center setNotificationCategories:[self createNotificationCategoryActionsForNewVersion]];
        
        [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
            if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) {  // 判斷是否是未選擇授權(quán)狀態(tài)
                // 若未授權(quán), 則請(qǐng)求授權(quán)
                [center requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
                        if (granted) {
                              NSLog(@"授權(quán)開(kāi)啟成功");
                        } else {
                              NSLog(@"授權(quán)開(kāi)啟失敗");
                        }
                }];
            }
        }];
    } else {
        //添加category: iOS 8.0以后
        UIUserNotificationCategory * category = [self adduserNotificationCategory];
        UIUserNotificationSettings * setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:[NSSet setWithObject:category]];
        [[UIApplication sharedApplication] registerUserNotificationSettings:setting];
    }
}

添加 category (即在通知欄重壓時(shí)設(shè)置其按鈕)


通知欄的通知
//添加category: iOS 10.0以后
-(NSSet *)createNotificationCategoryActionsForNewVersion{
    //注冊(cè)本地通知用到的Action
    //進(jìn)入app按鈕
    UNNotificationAction * localAction = [UNNotificationAction actionWithIdentifier:kCategoryActionIdentifierOpen title:@"查看通知" options:UNNotificationActionOptionForeground];
    
    //取消按鈕
    UNNotificationAction *localCancel = [UNNotificationAction actionWithIdentifier:kCategoryActionIdentifierCancel title:@"忽略" options:UNNotificationActionOptionDestructive];
    //將這些action帶入category
    UNNotificationCategory *localCategory = [UNNotificationCategory categoryWithIdentifier:kUNCategoryIdentifier actions:@[localAction,localCancel] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
    
    return [NSSet setWithObjects:localCategory,nil];
}

//添加category: iOS 8.0以后
- (UIMutableUserNotificationCategory *)adduserNotificationCategory {
    //UIMutableUserNotificationAction用來(lái)添加自定義按鈕
    UIMutableUserNotificationAction * responseAction = [[UIMutableUserNotificationAction alloc] init];
    responseAction.identifier = kCategoryActionIdentifierOpen;
    responseAction.title = @"查看通知";
    responseAction.activationMode = UIUserNotificationActivationModeForeground; //點(diǎn)擊的時(shí)候啟動(dòng)程序
    responseAction.authenticationRequired = YES;//需要解鎖權(quán)限
    
    UIMutableUserNotificationAction *deleteAction = [[UIMutableUserNotificationAction alloc] init];
    deleteAction.identifier = kCategoryActionIdentifierCancel;
    deleteAction.title = @"忽略";
    deleteAction.activationMode = UIUserNotificationActivationModeBackground; //點(diǎn)擊的時(shí)候不啟動(dòng)程序,后臺(tái)處理
    deleteAction.destructive = YES; //YES為紅色咬清,NO為藍(lán)色
    
    UIMutableUserNotificationCategory *category = [[UIMutableUserNotificationCategory alloc] init];
    category.identifier = kUNCategoryIdentifier;//用于將該 category 標(biāo)識(shí)的同時(shí)闭专,那一個(gè) notification 實(shí)例需要使用這個(gè) category 的 actions 也是傳入這個(gè)值給 notification 的。
    
    //UIUserNotificationActionContextDefault:默認(rèn)添加可以添加兩個(gè)自定義按鈕
    //UIUserNotificationActionContextMinimal:四個(gè)自定義按鈕
    [category setActions:@[responseAction, deleteAction] forContext:UIUserNotificationActionContextDefault];
    
    return category;
}

添加通知
??這里是點(diǎn)擊一個(gè)按鈕事件來(lái)添加通知的:

- (IBAction)addLocalNotification:(UIButton *)sender {
    NSDictionary * dict = @{@"businessId": @"kLocalNotificationTest",@"title": @"本地通知測(cè)試",@"memo": @"通知發(fā)送成功", @"startTime": @"2019-10-28 10:10:10"};
    NSDictionary * dict1 = @{@"businessId": @"kLocalNotificationTestOne",@"title": @"本地通知測(cè)試__One",@"memo": @"通知發(fā)送成功__One", @"startTime": @"2019-10-28 10:10:10"};
    NSArray * arr = @[dict, dict1];
    // 添加兩個(gè)通知
    for (NSDictionary * dataDic in arr) {
        NSData * data = [NSJSONSerialization dataWithJSONObject:dataDic options:NSJSONWritingPrettyPrinted error:nil];
        NSString * string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        [self addLocalNotificationWithData:string];
    }
}

/**
 添加本地通知

 @param dataString 通知的內(nèi)容: 字典序列化后的字符串
 */
- (void)addLocalNotificationWithData: (NSString *)dataString {
    NSData * data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
    
    if (@available(iOS 10.0, *)) {
        UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
        [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
            if (settings.authorizationStatus == UNAuthorizationStatusDenied) {
                NSLog(@"用戶未授權(quán)");
                return;
            }
            
            UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc] init];
            self.badgeNum++;
            content.badge = @(self.badgeNum);
            content.title = dict[@"title"];
            content.body = dict[@"memo"];
            content.userInfo = @{@"type" : kNotificationScheduleType, @"data": dict};;
            
            // 這個(gè)必須要加 且要和 UNNotificationCategory 的Identifier 保持一致
            content.categoryIdentifier = kUNCategoryIdentifier;
            
            UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.f repeats:NO];
            
            UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:dict[@"businessId"] content:content trigger:trigger];
            [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
                if (error) {
                    NSLog(@"添加本地通知報(bào)錯(cuò): %@", error);
                }
            }];
        }];
    } else {
        if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) {
            NSLog(@"用戶未授權(quán)");
            return;
        }
        
        UILocalNotification * notification = [[UILocalNotification alloc] init];
        notification.timeZone = [NSTimeZone defaultTimeZone];
        notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.f];
        
        notification.alertTitle = dict[@"title"];
        notification.alertBody = dict[@"memo"];
        self.badgeNum++;
        notification.applicationIconBadgeNumber = self.badgeNum;
        notification.soundName = UILocalNotificationDefaultSoundName;
        notification.alertAction = @"查看";
        
        notification.userInfo = @{@"type" : kNotificationScheduleType, @"data": dict};;
        notification.category = kUNCategoryIdentifier;
        
        [[UIApplication sharedApplication] scheduleLocalNotification:notification];
    }
}

通知的回調(diào)處理:

??iOS10.0 后是通知代理來(lái)回調(diào)的 UNUserNotificationCenterDelegate

// App在前臺(tái)獲取通知
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler  API_AVAILABLE(ios(10.0)){
    completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound);
}

// 點(diǎn)擊通知進(jìn)入App
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler  API_AVAILABLE(ios(10.0)){
    completionHandler();
    
    NSDictionary * userInfo = response.notification.request.content.userInfo;
    if(userInfo[@"type"] && [userInfo[@"type"] isEqualToString:kNotificationScheduleType]) {
        if([response.actionIdentifier isEqualToString:kCategoryActionIdentifierCancel] || [response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]) {
            // 通知欄點(diǎn)擊 忽略 或 清除 時(shí)的回調(diào)
        } else {
            //  點(diǎn)擊 打開(kāi) 或 查看 時(shí)的回調(diào)
        }
    }
}

??iOS10.0 以前的通知回調(diào):

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 本地通知 + App未存活
    UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    if (notification.userInfo) {
        // 處理回調(diào)回來(lái)的通知事件
    }
    return YES;
}
// APP在前臺(tái)運(yùn)行中收到 本地通知 時(shí)調(diào)用, 以及App 處于后臺(tái)掛起(suspended)狀態(tài)旧烧,但未 terminated 時(shí)喻圃,點(diǎn)擊通知啟動(dòng)都是這個(gè)方法進(jìn)行響應(yīng)
// 即 App 存活
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
    
}

//調(diào)用過(guò)用戶注冊(cè)通知方法之后執(zhí)行(也就是調(diào)用完registerUserNotificationSettings:方法之后執(zhí)行)
-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
    
}

// APP 將要進(jìn)入前臺(tái)時(shí)調(diào)用
- (void)applicationWillEnterForeground:(UIApplication *)application {
    //  設(shè)置App圖標(biāo)通知數(shù)量顯示
}

//iOS 8 ~ 9 ,當(dāng)點(diǎn)擊本地通知自定義的響應(yīng)按鈕(action btn)時(shí)粪滤,根據(jù)按鈕的 activeMode 模式斧拍,回調(diào)以下方法
//1. ActivationModeForeground 的 action , 會(huì)啟動(dòng) App 同時(shí)回調(diào)方法
//2. ActivationModeBackground 的 action 不啟動(dòng) App 讓 App 在 background 下回調(diào)方法
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler {
    
    if ([identifier isEqualToString:@"kCategoryActionIdentifierOpen"]) {
        //ActivationModeForeground 的 action , 啟動(dòng) App 讓 App 在 Foreground 下響應(yīng)
        
    } else {
        //ActivationModeBackground 的 action 不啟動(dòng) App 讓 App 在 background 下響應(yīng)
        NSLog(@"%s  -- %@  -- identifier %@ --- thread %@", __func__, notification, identifier, [NSThread currentThread]);
        
//        //下面代碼用于測(cè)試,退出 App 后接收到 本地通知時(shí)杖小,點(diǎn)擊后臺(tái)action時(shí)是否執(zhí)行了這個(gè)響應(yīng)方法肆汹。實(shí)測(cè)執(zhí)行了的
//        [[NSUserDefaults standardUserDefaults] setObject:@"ActivationModeBackground 的 action 不啟動(dòng) App 讓 App 在 background 下響應(yīng)" forKey:@"IGNOREKEY"];
//        [[NSUserDefaults standardUserDefaults] synchronize];
        
    }
    completionHandler(); //根據(jù)Action btn 的 identifier 處理自定義事件后應(yīng)該馬上調(diào)用 completionHandler block,如果調(diào)用 completionHandler block 失敗的話,App 會(huì)立即 terminated予权。
}

移除通知 或 移除指定通知

#pragma mark -- 移除本地通知
/**
 移除指定的通知

 @param identifierStr 通知的唯一標(biāo)識(shí) 或 信息
 */
- (void)removeNotificationWithIdentifier:(NSString *)identifierStr {
    //獲取本地通知數(shù)組 (該數(shù)組會(huì)持有需要重復(fù) fired 的 已被 copy 的 notification 對(duì)象昂勉,用于到達(dá)下次間隔時(shí)再 fire, 如果不需要重復(fù)的 notification,即 notification.repeatInterval = 0 的話扫腺,該 notification fire 之后不會(huì)被 copy 保留到這個(gè)數(shù)組里)
    //本地通知最多只能有64個(gè)岗照,超過(guò)會(huì)被系統(tǒng)忽略
    if (@available(iOS 10.0, *)) {
        [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[identifierStr]];
    } else {
        NSArray *notifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
        for (UILocalNotification * notification in notifications) {
            NSString * businessId = notification.userInfo[@"businessId"];
            if ([identifierStr isEqualToString:businessId]) {
                [[UIApplication sharedApplication] cancelLocalNotification:notification];
            }
        }
    }
    
    /*
     執(zhí)行取消本地通知的場(chǎng)景:
     1. 已經(jīng)響應(yīng)過(guò)的本地通知,需要取消笆环。
     2. 已經(jīng)遞交到 application 的攒至,但在 fire 之前 確定要取消的通知,需要取消躁劣。如提醒任務(wù)的取消迫吐,或更改提醒時(shí)間(此時(shí)應(yīng)該是新的一個(gè)本地通知了)
     */
}

/**
 移除所有的通知
 */
- (void)removeAllNotification {
    if (@available(iOS 10.0, *)) {
        [[UNUserNotificationCenter currentNotificationCenter] removeAllPendingNotificationRequests];
    } else {
        [[UIApplication sharedApplication] cancelAllLocalNotifications];
    }
}

因代碼是在項(xiàng)目里抽取的, 有不正之處歡迎指證...

***全部代碼:

AppDelegate.h

#import "AppDelegate.h"
#import "LocalNotification/LocalNotificationManager.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [[LocalNotificationManager shareLocalNotification] checkNotificationAuthorization];
    
    [[LocalNotificationManager shareLocalNotification] removeAllNotification];
    
    // 本地通知 + App未存活
    UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    if (notification.userInfo) {
        [[NSUserDefaults standardUserDefaults] setObject:notification.userInfo forKey:@"localNotificationKey"];
        [[NSUserDefaults standardUserDefaults] synchronize];
        
    }

    return YES;
}

// APP在前臺(tái)運(yùn)行中收到 本地通知 時(shí)調(diào)用, 以及App 處于后臺(tái)掛起(suspended)狀態(tài),但未 terminated 時(shí)账忘,點(diǎn)擊通知啟動(dòng)都是這個(gè)方法進(jìn)行響應(yīng)
// 即 App 存活
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
    [[LocalNotificationManager shareLocalNotification] receiveLocalNotificationWithUserInfo:notification.userInfo];
}

//調(diào)用過(guò)用戶注冊(cè)通知方法之后執(zhí)行(也就是調(diào)用完registerUserNotificationSettings:方法之后執(zhí)行)
-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
    
    NSLog(@"%s",__func__);
}

// APP 將要進(jìn)入前臺(tái)時(shí)調(diào)用
- (void)applicationWillEnterForeground:(UIApplication *)application {
    
    // 設(shè)置這個(gè)后, 后導(dǎo)致通知欄里的通知, 點(diǎn)擊其中任何一個(gè)后, 其它通知都會(huì)消失...
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
    [LocalNotificationManager shareLocalNotification].badgeNum = 0;
}

//iOS 8 ~ 9 志膀,當(dāng)點(diǎn)擊本地通知自定義的響應(yīng)按鈕(action btn)時(shí)熙宇,根據(jù)按鈕的 activeMode 模式,回調(diào)以下方法
//1. ActivationModeForeground 的 action , 會(huì)啟動(dòng) App 同時(shí)回調(diào)方法
//2. ActivationModeBackground 的 action 不啟動(dòng) App 讓 App 在 background 下回調(diào)方法
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler {
    
    if ([identifier isEqualToString:@"kCategoryActionIdentifierOpen"]) {
        //ActivationModeForeground 的 action , 啟動(dòng) App 讓 App 在 Foreground 下響應(yīng)
        [[LocalNotificationManager shareLocalNotification] receiveLocalNotificationWithUserInfo:notification.userInfo];
    } else {
        
        NSInteger badgeNumber = [LocalNotificationManager shareLocalNotification].badgeNum;
        badgeNumber = badgeNumber > 0 ? --badgeNumber : 0;
        [LocalNotificationManager shareLocalNotification].badgeNum = badgeNumber;
        [[UIApplication sharedApplication] setApplicationIconBadgeNumber:badgeNumber];
        
        //ActivationModeBackground 的 action 不啟動(dòng) App 讓 App 在 background 下響應(yīng)
        NSLog(@"%s  -- %@  -- identifier %@ --- thread %@", __func__, notification, identifier, [NSThread currentThread]);
        
//        //下面代碼用于測(cè)試溉浙,退出 App 后接收到 本地通知時(shí)烫止,點(diǎn)擊后臺(tái)action時(shí)是否執(zhí)行了這個(gè)響應(yīng)方法。實(shí)測(cè)執(zhí)行了的
//        [[NSUserDefaults standardUserDefaults] setObject:@"ActivationModeBackground 的 action 不啟動(dòng) App 讓 App 在 background 下響應(yīng)" forKey:@"IGNOREKEY"];
//        [[NSUserDefaults standardUserDefaults] synchronize];
        
    }
    
    completionHandler(); //根據(jù)Action btn 的 identifier 處理自定義事件后應(yīng)該馬上調(diào)用 completionHandler block,如果調(diào)用 completionHandler block 失敗的話戳稽,App 會(huì)立即 terminated烈拒。

}

@end

// 抽取的通知管理單例

LocalNotificationManager.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

typedef void(^LocalNotificationManagerBlock)(NSString * userInfoString);

@interface LocalNotificationManager : NSObject

// 消息的數(shù)量
@property (nonatomic, assign, readwrite) NSInteger badgeNum;
@property (nonatomic, copy) LocalNotificationManagerBlock managerBlock;


+ (instancetype)shareLocalNotification;


/**
 檢查本地通知的授權(quán)
 */
- (void)checkNotificationAuthorization;

/**
 添加本地通知

 @param dataString 通知的內(nèi)容: 字典序列化后的字符串
 */
- (void)addLocalNotificationWithData: (NSString *)dataString;


/**
 移除指定的通知

 @param identifierStr 通知的唯一標(biāo)識(shí) 或 信息
 */
- (void)removeNotificationWithIdentifier: (NSString *)identifierStr;


/**
 移除所有的通知
 */
- (void)removeAllNotification;


/**
 收到通知后的回調(diào)

 @param block block回調(diào)
 */
- (void)handleLocalNotificationWithBlock: (LocalNotificationManagerBlock)block;


/**
 收到通知后的數(shù)據(jù)處理

 @param userInfo 通知攜帶的數(shù)據(jù)
 */
- (void)receiveLocalNotificationWithUserInfo: (NSDictionary *)userInfo;


@end

NS_ASSUME_NONNULL_END


LocalNotificationManager.m

#import "LocalNotificationManager.h"
#import <UserNotifications/UserNotifications.h>
#import <UIKit/UIKit.h>

static NSString * const kNotificationScheduleType = @"kNotificationScheduleType";
static NSString * const kUNCategoryIdentifier = @"kUNCategoryIdentifier";
static NSString * const kCategoryActionIdentifierOpen = @"kCategoryActionIdentifierOpen";
static NSString * const kCategoryActionIdentifierCancel = @"kCategoryActionIdentifierCancel";

@interface LocalNotificationManager()<UNUserNotificationCenterDelegate>


@end

@implementation LocalNotificationManager

+ (instancetype)shareLocalNotification {
    static LocalNotificationManager * _instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[LocalNotificationManager alloc] init];
    });
    return _instance;
}

- (void)checkNotificationAuthorization {
    if (@available(iOS 10.0, *)) {
        UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
        // 這兩個(gè)必須要寫(xiě)在上面  每次程序重新打開(kāi)時(shí)都得設(shè)置一遍...
        center.delegate = self;
        [center setNotificationCategories:[self createNotificationCategoryActionsForNewVersion]];
        
        [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
            if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) {  // 判斷是否是未選擇授權(quán)狀態(tài)
                [center requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
                }];
            }
        }];
    } else {
        UIUserNotificationCategory * category = [self adduserNotificationCategory];
        UIUserNotificationSettings * setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:[NSSet setWithObject:category]];
        [[UIApplication sharedApplication] registerUserNotificationSettings:setting];
    }
}

- (void)addLocalNotificationWithData: (NSString *)dataString {
    NSData * data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
    
    NSTimeInterval noticInterval = [self diffNowDateWithNotificationTime:dict[@"startTime"]];
    if (noticInterval <= 0) {
        return;
    }
    
    if (@available(iOS 10.0, *)) {
        UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
        [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
            if (settings.authorizationStatus == UNAuthorizationStatusDenied) {
                NSLog(@"用戶未授權(quán)");
                return;
            }
            
            UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc] init];
            self.badgeNum++;
            content.badge = @(self.badgeNum);
            content.title = dict[@"title"];
            content.body = dict[@"memo"];
            content.userInfo = @{@"type" : kNotificationScheduleType, @"data": dict};;
            
            // 這個(gè)必須要加 且要和 UNNotificationCategory 的Identifier 保持一致
            content.categoryIdentifier = kUNCategoryIdentifier;
            
            UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.f repeats:NO];
            
            UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:dict[@"businessId"] content:content trigger:trigger];
            [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
                if (error) {
                    NSLog(@"添加本地通知報(bào)錯(cuò): %@", error);
                }
            }];
        }];
    } else {
        if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) {
            NSLog(@"用戶未授權(quán)");
            return;
        }
        
        UILocalNotification * notification = [[UILocalNotification alloc] init];
        notification.timeZone = [NSTimeZone defaultTimeZone];
        notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.f];
        
        notification.alertTitle = dict[@"title"];
        notification.alertBody = dict[@"memo"];
        self.badgeNum++;
        notification.applicationIconBadgeNumber = self.badgeNum;
        notification.soundName = UILocalNotificationDefaultSoundName;
        notification.alertAction = @"查看";
        
        notification.userInfo = @{@"type" : kNotificationScheduleType, @"data": dict};;
        notification.category = kUNCategoryIdentifier; // 與下面創(chuàng)建的category 的identifier必須保持一致
        
        [[UIApplication sharedApplication] scheduleLocalNotification:notification];
    }
}

//添加category: iOS 10.0以后
-(NSSet *)createNotificationCategoryActionsForNewVersion{
    //注冊(cè)本地通知用到的Action
    //進(jìn)入app按鈕
    UNNotificationAction * localAction = [UNNotificationAction actionWithIdentifier:kCategoryActionIdentifierOpen title:@"查看通知" options:UNNotificationActionOptionForeground];
    
    //取消按鈕
    UNNotificationAction *localCancel = [UNNotificationAction actionWithIdentifier:kCategoryActionIdentifierCancel title:@"忽略" options:UNNotificationActionOptionDestructive];
    //將這些action帶入category
    UNNotificationCategory *localCategory = [UNNotificationCategory categoryWithIdentifier:kUNCategoryIdentifier actions:@[localAction,localCancel] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
    
    return [NSSet setWithObjects:localCategory,nil];
}

//添加category: iOS 8.0以后
- (UIMutableUserNotificationCategory *)adduserNotificationCategory {
    //UIMutableUserNotificationAction用來(lái)添加自定義按鈕
    UIMutableUserNotificationAction * responseAction = [[UIMutableUserNotificationAction alloc] init];
    responseAction.identifier = kCategoryActionIdentifierOpen;
    responseAction.title = @"查看通知";
    responseAction.activationMode = UIUserNotificationActivationModeForeground; //點(diǎn)擊的時(shí)候啟動(dòng)程序
    responseAction.authenticationRequired = YES;//需要解鎖權(quán)限
    
    UIMutableUserNotificationAction *deleteAction = [[UIMutableUserNotificationAction alloc] init];
    deleteAction.identifier = kCategoryActionIdentifierCancel;
    deleteAction.title = @"忽略";
    deleteAction.activationMode = UIUserNotificationActivationModeBackground; //點(diǎn)擊的時(shí)候不啟動(dòng)程序,后臺(tái)處理
    deleteAction.destructive = YES; //YES為紅色广鳍,NO為藍(lán)色
    
    UIMutableUserNotificationCategory *category = [[UIMutableUserNotificationCategory alloc] init];
    category.identifier = kUNCategoryIdentifier;//用于將該 category 標(biāo)識(shí)的同時(shí),那一個(gè) notification 實(shí)例需要使用這個(gè) category 的 actions 也是傳入這個(gè)值給 notification 的吓妆。
    
    //UIUserNotificationActionContextDefault:默認(rèn)添加可以添加兩個(gè)自定義按鈕
    //UIUserNotificationActionContextMinimal:四個(gè)自定義按鈕
    [category setActions:@[responseAction, deleteAction] forContext:UIUserNotificationActionContextDefault];
    
    return category;
}


#pragma mark -- UNUserNotificationCenterDelegate
// App在前臺(tái)獲取通知
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler  API_AVAILABLE(ios(10.0)){
    completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound);
}

// 點(diǎn)擊通知進(jìn)入App
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler  API_AVAILABLE(ios(10.0)){
    completionHandler();
    
    NSDictionary * userInfo = response.notification.request.content.userInfo;
    if(userInfo[@"type"] && [userInfo[@"type"] isEqualToString:kNotificationScheduleType]) {
        if([response.actionIdentifier isEqualToString:kCategoryActionIdentifierCancel] || [response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]) {
            self.badgeNum = self.badgeNum > 0 ? --self.badgeNum : 0;
            [[UIApplication sharedApplication] setApplicationIconBadgeNumber:self.badgeNum];
        } else {
            [self receiveLocalNotificationWithUserInfo:userInfo[@"data"]];
        }
    }
}

#pragma mark -- 移除本地通知
- (void)removeNotificationWithIdentifier:(NSString *)identifierStr {
    
    //獲取本地通知數(shù)組 (該數(shù)組會(huì)持有需要重復(fù) fired 的 已被 copy 的 notification 對(duì)象赊时,用于到達(dá)下次間隔時(shí)再 fire, 如果不需要重復(fù)的 notification,即 notification.repeatInterval = 0 的話行拢,該 notification fire 之后不會(huì)被 copy 保留到這個(gè)數(shù)組里)
    //本地通知最多只能有64個(gè)祖秒,超過(guò)會(huì)被系統(tǒng)忽略
    if (@available(iOS 10.0, *)) {
        [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[identifierStr]];
    } else {
        NSArray *notifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
        for (UILocalNotification * notification in notifications) {
            NSString * businessId = notification.userInfo[@"businessId"];
            if ([identifierStr isEqualToString:businessId]) {
                [[UIApplication sharedApplication] cancelLocalNotification:notification];
            }
        }
    }
    
    
    //刪除指定通知
    //    [[UIApplication sharedApplication] cancelLocalNotification:notifications[0]];
    //刪除所有通知
//    [[UIApplication sharedApplication] cancelAllLocalNotifications];
    
    /*
     執(zhí)行取消本地通知的場(chǎng)景:
     1. 已經(jīng)響應(yīng)過(guò)的本地通知,需要取消舟奠。
     2. 已經(jīng)遞交到 application 的竭缝,但在 fire 之前 確定要取消的通知,需要取消沼瘫。如提醒任務(wù)的取消抬纸,或更改提醒時(shí)間(此時(shí)應(yīng)該是新的一個(gè)本地通知了)
     */
}

- (void)removeAllNotification {
    self.badgeNum = 0;
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
    if (@available(iOS 10.0, *)) {
        [[UNUserNotificationCenter currentNotificationCenter] removeAllPendingNotificationRequests];
    } else {
        [[UIApplication sharedApplication] cancelAllLocalNotifications];
    }
}

#pragma mark -- 處理收到的通知
- (void)handleLocalNotificationWithBlock:(LocalNotificationManagerBlock)block {
    NSDictionary * dict = [[NSUserDefaults standardUserDefaults] objectForKey:@"localNotificationKey"];
    if (dict) {
        [self receiveLocalNotificationWithUserInfo:dict];
    }
    self.managerBlock = block;
}

- (void)receiveLocalNotificationWithUserInfo:(NSDictionary *)userInfo {
    if (!userInfo) {
        [self showInfo:@"消息失敗"];
        return;
    }
    self.badgeNum = self.badgeNum > 0 ? --self.badgeNum : 0;
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:self.badgeNum];
    
    NSData * data = [NSJSONSerialization dataWithJSONObject:userInfo options:NSJSONWritingPrettyPrinted error:nil];
    NSString * string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    [self showInfo:string];
}

// 通知消息的顯示
- (void)showInfo:(NSString *)infoStr {
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:infoStr preferredStyle:UIAlertControllerStyleAlert];
    
    [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:NULL]];
    
    [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}

// 獲取當(dāng)前時(shí)間與指定時(shí)間之間的時(shí)間戳
- (CGFloat)diffNowDateWithNotificationTime: (NSString *)timeStr {
    NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:@"Asia/Beijing"];
    [formatter setTimeZone:timeZone];
    
    NSDate * noticDate = [formatter dateFromString:timeStr];
    
    NSTimeInterval noticInterval = [noticDate timeIntervalSince1970] * 1;
    NSTimeInterval nowInterval = [[NSDate date] timeIntervalSince1970] * 1;
    
    return noticInterval - nowInterval;
}
@end

添加通加的數(shù)據(jù)格式:

- (IBAction)addLocalNotification:(UIButton *)sender {
    NSDictionary * dict = @{@"businessId": @"kLocalNotificationTest",@"title": @"本地通知測(cè)試",@"memo": @"通知發(fā)送成功", @"startTime": @"2019-10-28 10:10:10"};
    NSDictionary * dict1 = @{@"businessId": @"kLocalNotificationTestOne",@"title": @"本地通知測(cè)試__One",@"memo": @"通知發(fā)送成功__One", @"startTime": @"2019-10-28 10:10:10"};
    NSArray * arr = @[dict, dict1];
    
    for (NSDictionary * dataDic in arr) {
        NSData * data = [NSJSONSerialization dataWithJSONObject:dataDic options:NSJSONWritingPrettyPrinted error:nil];
        NSString * string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        [[LocalNotificationManager shareLocalNotification] addLocalNotificationWithData:string];
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末汞舱,一起剝皮案震驚了整個(gè)濱河市樟澜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌稽莉,老刑警劉巖膜蛔,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坛猪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡皂股,警方通過(guò)查閱死者的電腦和手機(jī)墅茉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)呜呐,“玉大人就斤,你說(shuō)我怎么就攤上這事∧⒓” “怎么了战转?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)以躯。 經(jīng)常有香客問(wèn)我槐秧,道長(zhǎng)啄踊,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任刁标,我火速辦了婚禮颠通,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘膀懈。我一直安慰自己顿锰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布启搂。 她就那樣靜靜地躺著硼控,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胳赌。 梳的紋絲不亂的頭發(fā)上牢撼,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音疑苫,去河邊找鬼熏版。 笑死,一個(gè)胖子當(dāng)著我的面吹牛捍掺,可吹牛的內(nèi)容都是我干的撼短。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼挺勿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼曲横!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起不瓶,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤胜榔,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后湃番,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體夭织,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年吠撮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了尊惰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泥兰,死狀恐怖弄屡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鞋诗,我是刑警寧澤膀捷,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站削彬,受9級(jí)特大地震影響全庸,放射性物質(zhì)發(fā)生泄漏秀仲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一壶笼、第九天 我趴在偏房一處隱蔽的房頂上張望神僵。 院中可真熱鬧,春花似錦覆劈、人聲如沸保礼。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)炮障。三九已至,卻和暖如春坤候,著一層夾襖步出監(jiān)牢的瞬間胁赢,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工铐拐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人练对。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓遍蟋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親螟凭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子虚青,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容