NSUserActivity的基本使用

简介

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
@UIApplicationMain
class 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 UIKit
class 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: - UITableViewDataSource
extension 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 UIKit

class 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,方便在搜索之后点击搜索结果重新定位当前界面。

相关界面效果如下:






  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值