NSUserActivity的基本使用

来源:互联网 发布:caffe经典模型实战pdf 编辑:程序博客网 时间:2024/05/01 18:45
简介

NSUserActivity并不是一个新的概念,在iOS8中就已经使用它来做Handoff,在iOS9中User Activities变的可以搜索,并且可以在每个Activity里加上Index用的Metadata。但是只能用在用户访问过的或者看见过的内容中。

一旦某些内容被记录进NSUserActivity,就可以在Spotlight和Safari中同时被搜索到。而且还能通过设置'Eligible For Public Indexing'来让这些被Index的内容传到Apple的云端Cloud Index里,从而实现每个用户都能搜索到这个内容。同时Apple也强调了隐私的保护。并不是所有内容都是Public的,同一个内容需要在云端被Index超过一个限额(具体多少没有公布),才会最后成为Public的内容。所以用户不用担心自己看到的内容成为公众都能搜索的内容。

NSUserActivity基本内容
 
NSUserActivity对象提供了一种轻量级的方式捕获APP的状态并存储可以在之后使用。我们可以使用activity对象捕获用户正在操作的信息,比如:查看APP内容、编辑文本、查看网页、看video等。当系统启动我们的APP之后,activity对象是可以获取的,APP能够使用activity对象的信息恢复activity对象到合理的状态。Spotlight也可以使用activity对象来为用户提高搜索结果。
 
在关键时刻创建NSUserActivity对象并注册它们到系统。例如:我们可能在用户打开网页、或APP移到后台、或用户在APP执行一些重要的任务的时候创建activity对象。用户的activity对象并不打算跟踪APP中的每一个任务,所以我们不应该使用activity对象用于一些小的编辑或者次要的修改。相反,当用户想之后继续使用或在其它设备上使用,我们应该使用activity对象。我们也可以使用它们为Spotlight 提供更好的搜索结果。
 
当创建一个用户activity对象,做以下事情:
 
1
:使用合理的activity type来创建并初始化用户的activity对象。
2:设置用户activity对象的title
3
:使用一个或者多个下列属性来配置任务的对象:
      isEligibleForHandoff
      isEligibleForSearch
      isEligibleForPublicIndexing
4
:配置activity对象的相关属性
5
:对于用户 activity 对象,如果是配置用于搜索和公开位置,可以配置contentAttributeSet, keywords, webpageURL 等属性以至于Spotlight 能够索引对象。
6
:调用becomeCurrent() 方法来注册用户 activity 对象
 
当我们创建了NSUserActivity对象,我们使用了具体的字符串标识了activity的类型。activity类型字符串是reverse-DNS格式。例如:当用户打开一个网页,我们可以指定activity的字符串为com.myCompany.myApp.OpenWebPage我们必须在Info.plist文件中包含NSUserActivityTypes 作为key值对应的activity的类型。系统根据该key的信息来确定你的APP是否有能力处理给定的用户activity对象。并且支持Handoff,支持SiriKit,支持提高搜索结果等。
 
下面看一个简单demo,直接使用NSUserActivity实现搜索功能

demo非常简单,当运行程序之后,显示的是一个包含几首歌的列表,点击进入是一个详情界面。然后我们可以Command-Shift-H回到home界面,然后滑动到搜索界面,搜索对应的title等关键字就可以看到相应的搜索结果。当点击搜索结果会调到APP的详情界面。原文地址为:这里

首先看一下AppDelegate.swift文件:

import UIKit@UIApplicationMainclass AppDelegate: UIResponder, UIApplicationDelegate {   var window: UIWindow?   func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {        return true    }    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {        let mainController = (window?.rootViewController as! UINavigationController).viewControllers.first        mainController?.restoreUserActivityState(userActivity)        return true    }}
   主要是关注func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping([Any]?) -> Void) -> Bool方法,当接受到数据相关联的用户activity时,该方法将会被调用。该方法提供了我们机会执行具体的任务来更新APP。如果我们并没有实现该方法中实现了该方法或者返回值为false,iOS将尝试为APP创建文本来打开URL,并且iOS知道APP并不能够处理当前activity。如果返回true表示APP能够处理当前activity。简单一点理解操作就是:当点击搜索之后的内容,会触发该方法,在这里我们可以进行相应的任务处理,demo中执行了页面的跳转。

ListViewController.swift文件 ,即列表页面

import UIKitclass ListViewController: UIViewController {    fileprivate var songList: [SongInfo] = []    //存储搜索item的标识符    private var searchSongIdentifier: Int?    @IBOutlet weak var tableView: UITableView!        override func viewDidLoad() {        super.viewDidLoad()        //添加数据,SongInfo是一个模型类,仅仅是包含了3个属性和初始化方法        songList.append(SongInfo(song: "Bob Dylan - Like a Rolling Stone", album: "Highway 61 Revisited (1965)", style: "Rock"))        songList.append(SongInfo(song: "John Lennon - Imagine", album: "Imagine (1971)", style: "Rock, Pop"))        songList.append(SongInfo(song: "Nirvana - Smells Like Teen Spirit", album: "Nevermind (1991)", style: "Rock"))    }    override func viewWillAppear(_ animated: Bool) {        if let indexPath = tableView.indexPathForSelectedRow {            tableView.deselectRow(at: indexPath, animated: false)        }        super.viewWillAppear(animated)    }    //当执行segue过渡时,会触发该方法,该方法主要是获取选中的位置,便于后续的搜索,以及页面跳转    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {        var songId: Int?        if let index = tableView.indexPathForSelectedRow?.row{           songId = index        }else{           songId = searchSongIdentifier        }        //设置对应的歌曲信息和位置        let controller = segue.destination as! DetailedViewController        controller.songInfo = songList[songId!]        controller.songIndex = songId!           }    //判断activity中的userInfo字典中是否包含index作为key所对应的值,如果存在,存储对应的值到searchSongIdentifier,并进行跳转    override func restoreUserActivityState(_ activity: NSUserActivity) {        if let index = activity.userInfo?["index"] as? Int{           searchSongIdentifier = index           self.performSegue(withIdentifier: "showDetail", sender: self)        }    }}//MARK: - UITableViewDataSourceextension ListViewController: UITableViewDataSource {    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {        return songList.count    }    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {        let CellIdentifier = "MyCell"        let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier)        cell!.textLabel!.text = songList[(indexPath as NSIndexPath).row].song        return cell!    } }
       该页面实现简单,主要是关注prepare和restoreUserActivityState方法,在直接点击列表后,会触发prepare方法,跳转到详情页面,并传递所需的数据。当用户点击搜索结果之后,由于触发func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping([Any]?) -> Void) -> Bool方法,方法中我们执行了restoreUserActivityState方法,这里会对获取index进行相应的判断,一旦获取对应的值,就跳转到对应的详情界面,即我们之前选中的页面。

DetailedViewController.swift文件

import UIKitclass DetailedViewController: UIViewController,NSUserActivityDelegate {        var songInfo: SongInfo!    var songIndex: Int?    @IBOutlet weak var songLabel: UILabel!    @IBOutlet weak var albumLabel: UILabel!    @IBOutlet weak var styleLabel: UILabel!    override func viewDidLoad() {        super.viewDidLoad()        songLabel.text = songInfo.song        albumLabel.text = songInfo.album        styleLabel.text = songInfo.style                //创建NSUserActivity对象,activityType代表activity的类型,值为reverse-DNS format。该值与nfo.plist值保持一致        let activity = NSUserActivity(activityType: "com.appsfoundation.search.song")        //activity的名称,当搜索的时候,我们可以看到对应的title        activity.title = songInfo.song        //在设置title之后,我们可以搜索title,为了提高搜索效果,所有设置一系列局部关键字帮助用户在搜索结果中找到activity        var keywords = songInfo.song.components(separatedBy: "")        keywords.append(songInfo.album)        keywords.append(songInfo.style)        activity.keywords = Set(keywords)        //该布尔值确定是否能够使用Handoff在其它设备使用该activity        activity.isEligibleForHandoff = true        //是否应该被添加到设备index        activity.isEligibleForSearch = true        //是否能够被所有的IOS公众用户访问        activity.isEligibleForPublicIndexing = true        //设置代理        activity.delegate = self        //是否activity需要被更新,如果为真,在activity被发送之前触发代理方法        activity.needsSave = true                //为了避免再indexing之前被释放,需要复制当前创建的activity带全局的userActivity(在UIResponder类中声明)。在创建之后,我们必须添加activity到设备的索引用于搜索结果        userActivity = activity        //标记userActivity为当前使用的activity        userActivity?.becomeCurrent()    }        //通知代理用户的activity将被存储,存储用户点击的index,用于点击搜索的内容定位具体的详情页面    func userActivityWillSave(_ userActivity: NSUserActivity) {        userActivity.userInfo = ["index" : songIndex!]    }}
该界面非常简单,主要是创建NSUserActivity对象,并设置相应的属性,在代理方法中使用userActivity中的userInfo字典存储由上级传入选中的位置index,方便在搜索之后点击搜索结果重新定位当前界面。

相关界面效果如下:






0 0