这里说的通知是指推送通知,包括了本地通知、远程通知,跟那个 NSNotification 是有区别的。NSNotification 是不可见的,抽象的,一般事件传递、数据传递会使用到它;而推送通知是可见的。 推送通知可以让不在前台运行的 App,告知用户 App 内部发生了什么事情。

在 iOS 中,推送通知分为两种:

本地推送通知(Local Notification)

本地推送通知就是指那种不需要联网、不需要服务器支持就可以发出的推送通知。比如一些番茄闹钟、To-Do 应用都是用本地推送通知给用户发出通知的。
注意:

  • In iOS 8.0 and later, your application must register for user notifications using -[UIApplication registerUserNotificationSettings:] before being able to schedule and present UILocalNotifications.
  • In iOS 10.0 and later, use UserNotifications Framework’s UNNotificationRequest.

如果需要支持 iOS 8 或者 iOS 9 版本的话,本地推送通知的简单实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 需要在合适的时机注册 UIUserNotificationSettings,不一定需要在 AppDelegate.m 里注册
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[application registerUserNotificationSettings:settings];
// 当 App 没有在运行的时候(或者 App 被杀死)的情况下,
// 直接打开应用时,launchOptions = nil
// 通过本地推送通知打开应用时,launchOptions 有值
// 别人应用打开此应用时,launchOptions 有值
// 所以需要通过 launchOptions 里具体的值来判断时什么方式进来的
NSLog(@"launchOptions -> %%", launchOptions);
return YES;
}
// 监听收到的本地推送通知
// ① 当 App 在前台时,不会弹出通知,直接回调此方法,applicationState 为 UIApplicationStateActive
// ② 当 App 在后台时(App 没有被杀死),当用户点击弹出来的通知进入前台时,会回调此方法,applicationState 为 UIApplicationStateInactive
// ③ 当 App 没有在运行的时候(或者 App 被杀死),当用户点击弹出来的通知进入前台时,是不会回调此方法的
// 如果希望能够像 To-Do 应用那样可以根据通知来跳转到详情页面的话,得在这个方法判断如果 applicationState 为 UIApplicationStateInactive 时候跳转,为 UIApplicationStateActive 时不跳转,然后第 ③ 中情况需要在上面的方法来判断 launchOptions 的值,当 launchOptions[UIApplicationLaunchOptionsLocalNotificationKey] 不为空时候跳转即可。
// 不过到了 iOS 10 这些方法都被废弃了,不用那么麻烦了👍
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
NSLog(@"local notification -> %@, %zd", notification.alertBody, application.applicationState);
}

注册 UIUserNotificationSettings 之后,方可发出通知,比如点击一个按钮,5s 后发出一个本地推送通知。

1
2
3
4
5
6
7
- (IBAction)fireLocalNotification {
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.0];
localNotification.alertBody = @"我只是一个本地通知啊🙈";
// UILocalNotification 还有其他的属性比如声音、标题等等,可以进去设置
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}

local_notification

现在 iOS 10 的占有率已经很高了,所以还是得适配一下 iOS 10 的(我现在开发的 App 直接最低要求 iOS 10🙊),简单使用:发通知之前需要用户授权:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#import "AppDelegate.h"
#import <UserNotifications/UserNotifications.h>
@interface AppDelegate () <UNUserNotificationCenterDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 请求用户通知授权
if (NSClassFromString(@"UNUserNotificationCenter")) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) {
NSLog(@"error -> %@", error);
}];
center.delegate = self;
}
return YES;
}
#pragma mark - <UNUserNotificationCenterDelegate>
// 具体可以查看英文注释,很详细了
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
NSLog(@"willPresentNotification -> %@", notification.request.content.subtitle);
// 确定处理方式
completionHandler(UNNotificationPresentationOptionAlert);
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
// 当应用在后台(没有被杀死),点击推送通知会进来这里,但不会回调上面的方法
NSLog(@"didReceiveNotificationResponse -> %@", response.notification.request.content.subtitle);
// 确定处理方式
completionHandler();
}
@end

设置通知内容等然后 Fire(不截图了):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (IBAction)fireLocalNotification {
if (NSClassFromString(@"UNUserNotificationCenter")) {
// 使用父类 UNNotificationContent 的话,属性都不能修改,因此是使用 UNMutableNotificationContent
UNMutableNotificationContent *notificationContent = [[UNMutableNotificationContent alloc] init];
notificationContent.title = @"我只是一个本地通知啊🙈";
notificationContent.subtitle = @"原来你是小猴子";
notificationContent.body = @"🍊🍎🍋,请你吃吧,不客气";
notificationContent.sound = [UNNotificationSound defaultSound];
// 1. 不能直接使用父类 UNNotificationTrigger
// 2. time interval must be at least 60 if repeating
UNTimeIntervalNotificationTrigger *notificationTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.0 repeats:NO];
UNNotificationRequest *notificationRequest = [UNNotificationRequest requestWithIdentifier:@"Ya!" content:notificationContent trigger:notificationTrigger];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:notificationRequest withCompletionHandler:^(NSError * _Nullable error) {
NSLog(@"error -> %@", error);
}];
}
}

远程推送通知(Remote Notification)