本地通知
修改模拟器的语言.png//发送本地通知
func sendLocalNotification() {
//本地通知的设置
UNUserNotificationCenter.current().getNotificationSettings { (setting) in
//本地通知的状态
switch setting.authorizationStatus {
case .notDetermined:
print("未曾向用户请求授权")
self.requestAuth()
case .denied:
print("用户不允许通知")
DispatchQueue.main.async {
let alert = UIAlertController(title: "大额红包强", message: "建议执行以下操作:点击下方设置->通知->允许通知", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "取消", style: .cancel, handler: nil)
let settingAction = UIAlertAction(title: "设置", style: .default) { (action) in
UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
}
alert.addAction(cancelAction)
alert.addAction(settingAction)
}
case .authorized:
print("用户允许通知 可以给用户发送通知了")
self.scheduleLocalNotifications(setting)
case .provisional:
print("临时通知")
case .ephemeral:
//iOS14以上
print("临时授权应用程序发布通知。仅适用于应用程序剪辑")
@unknown default:
break
}
}
}
//请求授权
func requestAuth() {
//通知授权
UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge]) { (granted, error) in
if let error = error {
print(error.localizedDescription)
}
//判断用户是否授权了
if granted {
print("进行了授权")
}
}
}
//发送通知
func scheduleLocalNotifications(_ setting: UNNotificationSettings) {
if setting.alertSetting == .disabled {
print("用户关闭了横幅通知")
}
if setting.notificationCenterSetting == .disabled {
print("用户关闭了通知中心")
}
if setting.lockScreenSetting == .disabled {
print("用户关闭了锁屏通知")
}
let content = UNMutableNotificationContent()
content.title = "收到了一笔汇款"
content.body = "xx给你汇款100w元"
content.badge = 1
content.sound = .default
content.sound = UNNotificationSound(named: UNNotificationSoundName("xxx.caf"))
//添加多媒体附件
do{
let image = try UNNotificationAttachment(identifier: "image", url: Bundle.main.url(forResource: "testimage", withExtension: "png")!, options: [UNNotificationAttachmentOptionsThumbnailClippingRectKey:CGRect(x: 0, y: 0, width: 0.25, height: 0.25).dictionaryRepresentation])
/*
[UNNotificationAttachmentOptionsThumbnailClippingRectKey:CGRect(x: 0, y: 0, width: 0.25, height: 0.25).dictionaryRepresentation] 裁剪图片
*/
let video = try UNNotificationAttachment(identifier: "video", url: Bundle.main.url(forResource: "testvideo", withExtension: "mp4")!, options: [UNNotificationAttachmentOptionsThumbnailTimeKey:10])
/*
[UNNotificationAttachmentOptionsThumbnailTimeKey:10] 第10秒就作为画面就会作为缩略图画面进行呈现
*/
let audio = try UNNotificationAttachment(identifier: "audio", url: Bundle.main.url(forResource: "testaudio", withExtension: "mp3")!)
content.attachments = [video]//一般只能添加一个附件
}catch{
print(error.localizedDescription)
}
//隔多少秒之后进行发送通知
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
//日历触发器
// let dateComponents = DateComponents(calendar: Calendar.current,hour: 20, minute: 55 weekday: 5)
// let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
//位置触发器
// let regin = CLCircularRegion(
// center: CLLocationCoordinate2D(latitude: 37.335400, longitude: -122.009201),
// radius: 2000,
// identifier: "AppleLocation")
// regin.notifyOnExit = false
// regin.notifyOnEntry = true
// let reginTrigger = UNLocationNotificationTrigger(region: regin, repeats: true)
UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "x", content: content, trigger: trigger)) { (error) in
if let error = error {
print(error.localizedDescription)
}
}
}
隐式通知
UNUserNotificationCenter.current().requestAuthorization(options: [.provisional]) { (granted, error) in
if let error = error {
print(error.localizedDescription)
}
//判断用户是否授权了
if granted {
print("进行了授权")
}
}
数组内只要加入provisional,那么就不需要用户授权就可以发送通知 通知内容只能显示在通知中心,用户可以修改为显示通知
通知附加项
UNUserNotificationCenter.current().requestAuthorization(options: [.providesAppNotificationSettings]) { (granted, error) in
if let error = error {
print(error.localizedDescription)
}
//判断用户是否授权了
if granted {
print("进行了授权")
}
}
可以在通知界面添加出一个新的界面
1.需要在AppDelegate里面添加
UNUserNotificationCenter.current().delegate = self
2.实现代理方法
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) {
//相关设置
}
}
image.png3.在func sceneDidBecomeActive(_ scene: UIScene) { }
方法中跳转到自己的设置页面 设置可以关闭不同的通知
紧急通知
1.在苹果官网进行申请
2.根据苹果回复的文件进行下一步操作
UNUserNotificationCenter.current().requestAuthorization(options: [.criticalAlert]) { (granted, error) in
if let error = error {
print(error.localizedDescription)
}
//判断用户是否授权了
if granted {
print("进行了授权")
}
}
func sendNotifications() {
UNUserNotificationCenter.current().getNotificationSettings { (setting) in
switch setting.criticalAlertSetting {
case .notSupported:
print("notSupported")
case .disabled:
print("disabled")
case .enabled:
print("enabled")
let content = UNMutableNotificationContent()
content.title = "xxx"
content.body = "xxxxxx"
content.sound = .defaultCritical
content.sound = .defaultCriticalSound(withAudioVolume: 1)
content.sound = .criticalSoundNamed(UNNotificationSoundName("xx"))
content.sound = .criticalSoundNamed(UNNotificationSoundName("xx"), withAudioVolume: 1)
@unknown default:
break
}
}
可交互式通知
1.需要在AppDelete里面先设置好按钮
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//请求授权
requestAuth()
setNotificationCategory()
UNUserNotificationCenter.current().delegate = self
}
2.必须在AppDelegate里面提前设置好category 可在程序中其他地方调用
identifier必须设置为整个app唯一 以免出现异常
func setNotificationCategory() {
//identifier 必须整个app唯一
let acceptAction = UNNotificationAction(identifier: "ACEPT_ACTION", title: "同意", options: [.foreground])
//.foreground 打开app
let declineAction = UNNotificationAction(identifier: "DECLINE_ACTION", title: "拒绝", options: [.destructive,.authenticationRequired])
//.destructive 拒绝按钮
//.authenticationRequired 必须解锁手机
//临时回复按钮
let replyAction = UNTextInputNotificationAction(identifier: "REPLY_ACTION", title: "回复")
// let replyAction = UNTextInputNotificationAction(
// identifier: "REPLY_ACTION",
// title: "回复",
// options: [],
// textInputButtonTitle: "发送不", //发送按钮文字
// textInputPlaceholder: "你是谁")
let categoty = UNNotificationCategory(identifier: "FRIEND_REQUEST", actions: [acceptAction,declineAction,replyAction], intentIdentifiers: [],options: [.customDismissAction])
UNUserNotificationCenter.current().setNotificationCategories([categoty])
}
3.请求授权
func requestAuth() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge]) { (granted, error) in
if let error = error {
print(error.localizedDescription)
return
}
if granted {
print("用户允许")
let content = UNMutableNotificationContent()
content.title = "好友申请"
content.body = "申请加为好友"
content.sound = .default
content.categoryIdentifier = "FRIEND_REQUEST"
content.userInfo = ["FRIEND_USER_ID": 99] //附带信息
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 4, repeats: false)
UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "x", content: content, trigger: trigger), withCompletionHandler: nil)
}
}
}
4.调用代理方法
//用户在前台
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
print("当app处于前台时,用户点击了通知,触发操作")
let content = notification.request.content
let userInfo = content.userInfo
if content.categoryIdentifier == "FRIEND_REQUEST" {
let requestUserID = userInfo["FRIEND_USER_ID"] as! Int
print("FRIEND_USER_ID 为 \(requestUserID)的人向当前用户发送了好友请求 我们可以更新一些UI")
completionHandler([.sound])
return
}else{
//其他类型的通知
}
completionHandler([]) //展示横幅和声音
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
print("当app处于后台或关闭状态(无论前台或者后台)时,用户点击了通知,我们要做的操作")
let content = response.notification.request.content
let userInfo = content.userInfo
if content.categoryIdentifier == "FRIEND_REQUEST" {
let requestUserID = userInfo["FRIEND_USER_ID"] as! Int
switch response.actionIdentifier {
case "ACEPT_ACTION":
print("当前用户接受了FRIEND_USER_ID 为 \(requestUserID)的好友申请")
break
case "DECLINE_ACTION":
print("当前用户拒绝了FRIEND_USER_ID 为 \(requestUserID)的好友申请")
break
case "REPLY_ACTION":
let replyText = (response as! UNTextInputNotificationResponse).userText
print("当前用户向FRIEND_USER_ID 为 \(requestUserID)的人发送了'\(replyText)'的回复")
break
case UNNotificationDefaultActionIdentifier:
print("用户单纯点击通知进入了我们的app")
break
case UNNotificationDismissActionIdentifier: //必须设置UNNotificationCategory的options里面包含customDismissAction 才会起作用
print("用户点击了通知的清除按钮")
break
default:
break
}
}
}
UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "x", content: content, trigger: trigger), withCompletionHandler: nil) 当identifier是一样的话 连续发通知 后面的通知会覆盖掉前面的
分组通知
如果没有设置分组 就是自动分组 通知会全部重叠在一起
设置通知的content.threadIdentifier就可以实现通知的不同分组
let content = UNMutableNotificationContent()
content.title = "好友申请"
content.body = "申请加为好友"
content.sound = .default
content.categoryIdentifier = "FRIEND_REQUEST"
content.userInfo = ["FRIEND_USER_ID": 99] //附带信息
content.threadIdentifier = "FRIEND"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 4, repeats: false)
UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "x", content: content, trigger: trigger), withCompletionHandler: nil)
当用户在设置里面设置了按app分通知 我们做的操作就无效了
通知概要
1.设置category
let categotyFR = UNNotificationCategory(
identifier: "MOMENTS_UPDATE",
actions: [],
intentIdentifiers: [],
hiddenPreviewsBodyPlaceholder: nil,
categorySummaryFormat: "%u %@", //概要显示
options: [.customDismissAction])
UNUserNotificationCenter.current().setNotificationCategories([categoty,categotyFR])
2.发送通知
func sendMomentsUpdateNotifications() {
let content = UNMutableNotificationContent()
content.title = "朋友圈更新"
content.body = "XXX上传了9张图片"
content.sound = .default
content.categoryIdentifier = "MOMENTS_UPDATE"
content.userInfo = ["MOMENTS_UPDATE_USER_ID": 100]
content.threadIdentifier = "MOMENTS_UPDATE"
content.summaryArgument = "999" //内容
content.summaryArgumentCount = 10 //消息的权重
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 4, repeats: false)
UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "MOMENTS_UPDATE", content: content, trigger: trigger), withCompletionHandler: nil)
}
用户关闭通知预览时 显示的内容
当用户关闭预览时 点击通知会直接进入app内部 不会把折叠的通知展开显示
设置category的hiddenPreviewsBodyPlaceholder方可修改显示内容
let categotyFR = UNNotificationCategory(
identifier: "MOMENTS_UPDATE",
actions: [],
intentIdentifiers: [],
hiddenPreviewsBodyPlaceholder: "%u条新消息",//所有通知的数量
categorySummaryFormat: "%u %@", //被折叠的通知的数量
options: [.customDismissAction])
获取已发送通知和未发送通知
当app在前台 未设置时 若通知中心没有通知 是获取不到已发送通知和无法删除已发送通知的
func getPendingNotification() {
UNUserNotificationCenter.current().getPendingNotificationRequests { (requests) in
print("获取到所有未发送的通知 \(requests)")
}
}
func getDdeliveredNotification() {
UNUserNotificationCenter.current().getDeliveredNotifications { (notifications) in
print("获取到所有已发送的通知 \(notifications)")
}
}
所有Pending都是指非重复通知未被trigger+重复通知未被移除
Ddelivered是指在通知只能更新正在显示的通知
通知的更新
无论通知是否已经到达 都可以直接更新 只要identifier 相同就可以覆盖更新掉
let content = UNMutableNotificationContent()
content.title = "朋友圈更新"
content.body = "XXX上传了9张图片"
content.sound = .default
content.categoryIdentifier = "MOMENTS_UPDATE"
content.userInfo = ["MOMENTS_UPDATE_USER_ID": 100]
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 4, repeats: false)
UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "MOMENTS_UPDATE", content: content, trigger: trigger), withCompletionHandler: nil)
通知的删除
func removePendingNotification() {
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["MOMENTS_UPDATE"])
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
}
func removeDdeliveredNotification() {
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["MOMENTS_UPDATE"])
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
}
自定义通知
image.png1.新建target
image.png2.自定义通知的界面
设置图片的时候 如果显示的过大 可以在viewDidLoad()里面设置尺寸
override func viewDidLoad() {
super.viewDidLoad()
preferredContentSize = CGSize(width: 320, height: 320)
}
如果需要设置交互操作 必须在iOS12之后才有效
如果想要交互操作有效果 需要在plist文件添加属性UNNotificationExtensionUserInteractionEnabled
image.png3.发送的通知的identifier category的identifier 和plist文件里面的UNNotificationExtensionCategory的value必须一致 才会显示自定义通知界面
image.png4.由于自定义通知 下方会带上通知的内容 如果想通知的内容去掉 需要在plist里面设置UNNotificationExtensionDefaultContentHidden
image.png5.修改自定义通知上的app名称 需要修改plist里面的UNNotificationExtensionOverridesDefalutTitle为true
修改stroryboard里面的ViewController的title就是修改自定义通知上显示的app名称
image.png6.如果需要修改自定义通知上控件的赋值
在以上方法里面进行赋值才不会出现异常
7.播放视频
设置播放按钮
var mediaPlayPauseButtonFrame: CGRect {
CGRect(x: view.center.x - 25, y: view.center.y - 25, width: 50, height: 50)
}
var mediaPlayPauseButtonTintColor: UIColor {
.orange
}
var mediaPlayPauseButtonType: UNNotificationContentExtensionMediaPlayPauseButtonType {
.overlay //点击之后消失 再次点击就会出现
}
播放 暂停视频
func mediaPlay() {
}
func mediaPause() {
}
8.如果通知带有Action按钮
那么就会先触发
func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {}
根据这个方法里面的操作
再调用delegate里面的代理方法
func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {
switch response.actionIdentifier {
case "playAction":
//播放视频
extensionContext?.mediaPlayingStarted()//联动视频上的按钮
completion(.doNotDismiss)//通知不消失
case "inAppAction":
completion(.dismissAndForwardAction)//进入app
case "dismissAction":
completion(.dismiss)//通知消失
default:
break
}
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
if response.actionIdentifier == "inAppAction" {
print("从通知进入了app")
}
}
远程通知
image.png1.添加push
//注册通知
UIApplication.shared.registerForRemoteNotifications()
//通知注册成功后的回调
//deviceToken可以指定到特定的手机特定的app
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print(deviceToken.map{ String(format: "%02.2hhx",$0)}.joined())
}
//通知注册失败后的回调
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
}
2.远程通知和本地通知
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void)
里面的
let content = response.notification.request.content
let userInfo = content.userInfo
对于本地通知而言 userInfo是通知的userInfo的内容
对于远程通知而言 userInfo是通知的所有内容
静默推送
image.png1.添加backgroundMode
image.png
//静默推送的配置:
//1.Xcode中添加Background Modes的Capability,并打开Remote notifications
//2.payload的aps的值中不能有alert,sound,badge;必须有content-available,值设为1
//3.服务端向APNs发送的推送请求头中,设apns-push-type为background,设apns-topic为App的bundle ID,设apns-priority为5(低优先级)
//苹果只给30秒的时间处理请求
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
print(userInfo)
completionHandler(.noData)
}
在到达用户之前进行修改alert
image1.添加push
.添加push
image.png2.添加target
3.发送的通知必须有alert 和 mutable-content 才能进行拦截修改
{
"aps":{
"alert":{
"title":"好友申请",
"body":"马云申请加为好友"
},
"mutable-content":1
},
"requestUserID":88
}
4.把未达到的通知的图片改为网络图片
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
// Modify the notification content here...
if let imageID = bestAttemptContent.userInfo["imageID"] as? String,
let imageURLString = bestAttemptContent.userInfo["imageURLString"] as? String,
let imageRemoteURL = URL(string: imageURLString),
let att = saveToDisk(imageRemoteURL: imageRemoteURL, imageID: imageID)
{
bestAttemptContent.attachments = [att]
}else if let att = placeholderAtt(){
bestAttemptContent.attachments = [att]
}
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
if let att = placeholderAtt(){
bestAttemptContent.attachments = [att]
}
contentHandler(bestAttemptContent)
}
}
func saveToDisk(imageRemoteURL: URL, imageID: String)->UNNotificationAttachment?{
//定义文件夹和图片在本地的URL(2-1和2-3)
//2-1专门在Temp文件夹里面存图片的文件夹URL--temp/xxx
let imageTempDirURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(ProcessInfo.processInfo.globallyUniqueString)
//2-3图片在本地的URL--temp/xxx/yyy.jpg
let imageLocalURL = imageTempDirURL.appendingPathComponent("\(UUID().uuidString).jpg")
do {
//2-2创建文件夹
try FileManager.default.createDirectory(at: imageTempDirURL, withIntermediateDirectories: true)
//1-1获取图片data
let data = try Data(contentsOf: imageRemoteURL)
//3-1把图片用定义好的URL存到本地,待会就可以用这个URL取到图片了
try data.write(to: imageLocalURL)
let att = try UNNotificationAttachment(identifier: imageID, url: imageLocalURL)
return att
} catch {
print(error.localizedDescription)
}
return nil
}
//备选图片(下载图片失败或超时时使用)
func placeholderAtt()->UNNotificationAttachment?{
if let url = Bundle.main.url(forResource: "placeholder", withExtension: "jpg"),
let att = try? UNNotificationAttachment(identifier: "placeholder", url: url){
return att
}
return nil
}
网友评论