iOS 本地通知那些事 - 这块显卡有点冷
原文链接
从iOS8开始,本质上来说有两种通知:
本地通知(local notifications):由开发者定义,App触发。触发的时间是被事先安排好的。
远程通知(remote notifications):这种情况下,通知可以被分成两个类别:(a)推送通知(The push notifications),被服务器初始化,然后通过APNS,最终到达用户设备。(b)静默通知(The silent notifications),其实也是推送通知,但是他们并没有被展示给用户,而是立即被App处理以发起某项任务,最后当一切都完成时,一个本地通知 被显示以提示用户。
<!-- more -->
除了以上的2种以外,iOS8引入了地点通知(location notifications)。它其实也是本地通知(local notifications),但是他们只会在用户一个特定的地理或者iBeacon区域时,才会被触发。虽然我们看不到什么细节,地点通知 (location notifications)实现起来也很容易。
从iOS8开始,通知被加入了新的特性。简单地说,从现在开始,当一个通知被展示时,开发者可以指定用户可触发的具体的动作(actions),而且甚至不用启动App也可以处理这个通知。
关于本地通知
Alert or Banner:通知可以用alert或者banner来显示,这取决于用户在设置中得选择。
Sound: 当一个通知被送达时,你可以‘告诉’iOS播放一段自定义或者系统默认的声音。
Badge: 当通知到达时,一个badge数字会在App的图标上显示。当一个通知到达时,badge数字必增加1,当通知被处理后badge数字减1。当badge数字不为0或者为0,iOS会显示或者隐藏badge。
可以被安排的本地通知的数量并不是无限的,最多有64个本地通知可以被安排和展示。如果多余这个数字,所有超过这个数字的通知都会被废弃。尽管如此,无论通知是以什么样的形式被安排的,最早的那个会被最先展示。
规定通知类型
我们来创建一个叫setupNotificationSettings()
方法。
func setupNotificationSettings() { //规定通知类型 var notificationTypes:UIUserNotificationType = [.Alert,.Sound,.Badge] }
UIUserNotificationType
现在是一个结构体类型。它包含了通知的所有可能的类型。
创建通知动作
一个动作就是一个UIMutableUserNotificationAction
类的对象。UIMutableUserNotificationAction
是iOS8
新引入的类,有着许多有用的配置属性:
标示符(identifier): 字符串,标示了一个对于整个App唯一的字符串。很明显,你永远不应该在同一个App中定义两个同样地标示符。通过此标示符,我们可以决定在用户点击不同的通知时,调用哪个动作。
标题(title):用来在展示给用户的动作按钮上。可以是简单地或者本地化的字符串。为了让用户能马上理解动作的含义,一定要仔细考虑这个标题的值,最好是1到2个字符。
destructive: 布尔值。当设置为true时,通知中相应地按钮的背景色会变成红色。这只会在banner通知中出现。通常,当动作代表着删除、移除或者其他关键的动作是都会被标记为destructive以获得用户的注意。
authenticationRequired: 布尔值。当设置为true时,用户在点击动作之前必须确认自己的身份。当一个动作十分关键时这非常有用,因为为认证的操作有可能会破坏App的数据。
ActivationMode: 决定App在通知动作点击后是应该被启动还是不被启动。
behavior: iOS9 新特性 可以支持在用户通知中输入文本
parameters: iOS9 新特性
接下来我们来创建几种不同的动作:
//1 点击后消失,不会做任何事情 var justInformAction = UIMutableUserNotificationAction() justInformAction.identifier = "justInform" justInformAction.title = "OK" justInformAction.destructive = false justInformAction.authenticationRequired = false justInformAction.activationMode = .Background //2 启动应用 var secAction = UIMutableUserNotificationAction() secAction.identifier = "secAction" secAction.title = "OK" secAction.activationMode = .Foreground secAction.destructive = false secAction.authenticationRequired = false
我们把上边的两个方法放在setupNotificationSettings
中。
当一个通知的所有动作被配置好了之后,他们可以被包进一个类目(categories)里。如果你的通知支持动作,那么你就必须创建一个类目 (categories)。通常情况下一个类目(category)配对一个通知,假设一个App中得所有通知都支持动作,那么这个App也会有和通知一 样多的类目(categories)。
类目(category)就是一个 UIMutableUserNotificationCategory类的对象,这也是iOS8新引入的类。这个类只有一个属性和一个方法。标示符属性用 来表示一个唯一的类目(category),方法用来将多个动作包含进来。
我们来让我们来了解一下这个方法 (setActions):
public func setActions(actions: [UIUserNotificationAction]?, forContext context: UIUserNotificationActionContext)
第一个参数指明了需要包含进来的动作。是一个包含所有动作的数组,他们在数组中的顺序也代表着他们将会在一个通知中调用的先后顺序。
第二个参数非常重要。context形参是一个枚举类型,描述了通知alert显示时的上下文,有两个值:
Default
: 在屏幕的中央展示一个完整的alert。(未锁屏时)Minimal
: 展示一个banner alert。
在默认上下文(default context)中,类目最多接受4个动作,会以预先定义好的顺序依次在屏幕中央显示。在minimal上下文中,最多可以在banner alert中设置2个动作。注意在第二个情况中,你必须选择一个较为重要的动作以显示到banner通知里。接下来我们会将这两种情况都用代码实现。
var category = UIMutableUserNotificationCategory() category.identifier = "category1" category.setActions([justInformAction], forContext: .Default) category.setActions([secAction], forContext: .Minimal)
然后…这样就行啦,为一个通知相关的动作创建一个类目就这样完成了。
注册通知设置
通过上面的3个部分,我们已经将本地通知的所有新功能已经实现了。现在我们需要将这些设定注册到用户设置中。为了完成这个目标,我们将会用到 UIUserNotificationSettings类(iOS8新引入),然后在下面的init方法中,我们会指定通知类型和类目 (category)。
convenience init(forTypes types: UIUserNotificationType, categories: Set<UIUserNotificationCategory>?)
第一个参数是我们为通知设置的类型,第二个方法是一个集合(NSSet),在这个集合中必须包含一个App所有通知支持的类目。在本例中,我们只有一个类目,但是我们还是需要使用集合来传递它。
let newNotificationSettings = UIUserNotificationSettings(forTypes: notificationTypes, categories: [category]) //最后,让我们将它注册一下吧! UIApplication.sharedApplication().registerUserNotificationSettings(newNotificationSettings)
第一次启动App时上述代码就会执行,它会在用户设置中创建一条我们的App记录。
最后,在我展现一个完整的setupNotificationSettings(),还有一点需要注意。这个方法会在viewDidLoad方法中 被调用,这意味着每当App被启动的时候它都会执行一次。很显然一遍又一遍的设置同样地值是在做无用功,这样如果我们将上面的方法用一个guard判断执行一下的话就好了。
let notificationSettings = UIApplication.sharedApplication().currentUserNotificationSettings() guard notificationSettings?.types != .None else{return }
首先,我们通过UIApplication的类方法currentUserNotificationSettings()来获取通知的类型。通过这 个方法返回的UIUserNotificationSettings类的对象,我们可以检查它的types枚举属性。请记住这个属性为枚举类型。如果它的 值为None,那么通知类型就还没有被注册,然后我们就运行上面的方法来注册通知类型,否则什么也不做。
完整代码:
func setupNotificationSetings(){ let notificationSettings = UIApplication.sharedApplication().currentUserNotificationSettings() guard notificationSettings?.types != .None else{return }//规定通知类型 let notificationTypes:UIUserNotificationType = [.Alert,.Sound,.Badge] //创建通知动作 //1 点击后消失,不会做任何事情 let justInformAction = UIMutableUserNotificationAction() justInformAction.identifier = "justInform" //标识符 justInformAction.title = "OK" justInformAction.destructive = false justInformAction.authenticationRequired = false justInformAction.activationMode = .Background //2 启动应用 let secAction = UIMutableUserNotificationAction() secAction.identifier = "secAction" secAction.title = "OK" secAction.activationMode = .Foreground secAction.destructive = false secAction.authenticationRequired = false let category = UIMutableUserNotificationCategory() category.identifier = "category1" category.setActions([justInformAction,secAction], forContext: .Default) let newNotificationSettings = UIUserNotificationSettings(forTypes: notificationTypes, categories: [category]) UIApplication.sharedApplication().registerUserNotificationSettings(newNotificationSettings) }
安排本地通知
如果你在iOS之前的版本中使用过本地通知的话,你一定知道安排一个通知是很简单地事情。在iOS8,安排一个通知并没有什么变化。事实上,所有的基本设置都是一模一样的。唯一的新东西就是必须给一个通知设置一个类目,这样通知就能知道当用户点击的时候该启动哪些动作了。
我们会定义一个新的方法来配置和安排一个本地通知。在我们实现这个方法之前,我们先看看一个本地通知中得重要属性:
fireDate:一个通知应当被显示的日期和时间。NSDate对象。
alertBody:通知的内容。应当尽量的简洁明了,这样用户才能马上理解它。
alertAction:在默认情况下,点击一个banner通知会导致App启动。在以alert形式显示的通知中,会创建一个和这个动作对应 的按钮。在此属性中,你必须指定这个按钮的标题。
。。。等等
现在放我们定义这个方法配置这个通知。不用说先让我们创建一个UILocalNotification对象
func scheduleLocalNotification(){ let localNotification = UILocalNotification() localNotification.fireDate = NSDate() localNotification.alertBody = "Hellor World" localNotification.alertAction = "View" //我们必须指定用户点击通知后对应的类目动作。回忆一下,我们前面已经定义了一个类目和类目标示符,我们在这里就能使用到这个标示符了 localNotification.category = "category1" //最后,我们需要使用UIApplication的scheduleLocalNotification(_:) 方法来真正的安排一个通知,不然这个通知永远都不会“通知”到你啦。 UIApplication.sharedApplication().scheduleLocalNotification(localNotification) }
处理通知动作
现在关于通知,我们只差最后一个部分了,那就是处理用户点击通知相关按钮时候的各种动作。和往常一样,这里有几个主要的委托方法我们需要实现。
介绍几个代理方法,通过他们你可以方便的开发你的App。
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {}
第一个代理方法是关于通知设置的。这个代理方法在程序启动时被调用(不管是正常启动还是通过一个本地通知),包含了所有App通知的设置选项。
通过上述的方法,你可以得到所有UIUserNotificationSettings支持的类型。当你需要检查你的App所支持的通知和动作的类型时,这个方法非常有用。别忘了,用户可以通过用户设置来改变通知类型,所以我们不能保证,初始的通知类型一直都有效。
当你安排了一个通知之后,无论你的App是否在运行,这个通知都将被推送。通常情况下,开发者设置通知如何在App没有运行或者被挂起的时候被推 送,所有的代码实现也聚焦在这两个方面。但是,我们也应该处理当App在运行时通知如何被处理。感谢苹果,iOS SDK让这变得非常简单,有一个代理方法正可以处理这种情况:
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {}
当然在某些情况下在App运行时你并不需要处理通知。但是在另外一个情况下,上面的代理方法是处理通知动作的地方。
现在让我们来看看当用户点击了一个通知动作按钮后将会调用的代理方法。更具我们给动作设置的标示符(identifier),我们决定那个动作被调用,然后App就会执行对应的代码了。
我们将根据identifier
的值给每一种情况发送一个 NSNotification
,在ViewController
类中,我们监视这些NSNotification
,然后我们处理他们。
让我们从新的代理方法开始:
func application(application: UIApplication, handleActionWithIdentifier identifier: String?, forLocalNotification notification: UILocalNotification, completionHandler: () -> Void) { if identifier == "secAction"{ NSNotificationCenter.defaultCenter().postNotificationName("secaction", object: nil) } completionHandler() }
在上述几种情况中我们根据动作的的标示符,发送不同名称的NSNotification对象。注意到,我们在方法的结束调用了 completionHandler()方法,根据规定我们必须调用它,这样系统才能知道我们已经处理完了通知动作。在处理本地通知时,这个代理方法非常 重要,在这里你通过用户的点击执行相应地代码。
接下来,让我们打开ViewController.swift文件。首先,让我们监视我们之前发送的NSNotification。在viewDidLoad中加入下面的代码:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "secAction", name: "secaction", object: nil)
secAction
是我们自己要实现的方法。
func secAction(){ print("我是 notification 激活的方法") }
到这里我们的基本介绍就完了。
如何在 iOS 8 中使用 Swift 实现本地通知(上)
如何在 iOS 8 中使用 Swift 实现本地通知(下)
参考
做好了本地推送不是不是也要考虑一下远程的推送。
推送笔记