介绍
在WWDC 2015上,Apple正式发布了iOS 9 。 除了许多新功能和改进之外,此更新还使开发人员有机会通过Spotlight搜索使应用程序的内容更易于发现和访问。 iOS 9中提供的新API允许您索引应用程序中的任何内容或界面状态,从而使用户可以通过Spotlight访问它。 这些新的搜索API的三个组件是:
-
NSUserActivity
类,用于查看已查看的应用程序内容 - Core Spotlight框架,该框架适用于任何应用程序内容
- 网络标记,设计用于具有在网站上镜像的内容的应用程序
在本教程中,我将向您展示如何在自己的应用程序中使用NSUserActivity
类和Core Spotlight框架。
先决条件
本教程要求您在OS X 10.10或更高版本上运行Xcode 7。 要跟我一起学习,还需要从GitHub下载入门项目。
1.使用NSUserActivity
在本教程的第一部分中,我将向您展示如何通过NSUserActivity
类为应用程序的内容编制索引。 该API与去年在iOS 8中引入的Handoff功能相同,可处理保存和还原应用程序的当前状态。
如果您以前从未使用过NSUserActivity
,那么建议您先阅读我的教程 ,其中介绍Handoff和NSUserActivity
的基础知识,然后再继续学习。
在编写任何代码之前,请打开starter项目并在iOS Simulator或测试设备上运行该应用程序。 在此阶段,您将看到该应用程序仅显示四个电视节目的列表以及每个节目的详细信息页面。
![显示清单](https://cms-assets.tutsplus.com/uploads/users/855/posts/24375/image/Simulator%20Screen%20Shot%2010%20Jul%202015,%201.08.12%20PM.png)
![查看详细](https://i-blog.csdnimg.cn/blog_migrate/3439810f9616edcf54c7d8d019dda12f.png)
首先,打开启动程序项目并导航到DetailViewController.swift 。 用以下实现替换DetailViewController
类的configureView
方法:
func configureView() {
// Update the user interface for the detail item.
if self.nameLabel != nil && self.detailItem != nil {
self.nameLabel.text = detailItem.name
self.genreLabel.text = detailItem.genre
let dateFormatter = NSDateFormatter()
dateFormatter.timeStyle = .ShortStyle
self.timeLabel.text = dateFormatter.stringFromDate(detailItem.time)
let activity = NSUserActivity(activityType: "com.tutsplus.iOS-9-Search.displayShow")
activity.userInfo = ["name": detailItem.name, "genre": detailItem.genre, "time": detailItem.time]
activity.title = detailItem.name
var keywords = detailItem.name.componentsSeparatedByString(" ")
keywords.append(detailItem.genre)
activity.keywords = Set(keywords)
activity.eligibleForHandoff = false
activity.eligibleForSearch = true
//activity.eligibleForPublicIndexing = true
//activity.expirationDate = NSDate()
activity.becomeCurrent()
}
}
在视图控制器中配置标签的代码未更改,但让我们逐步介绍用户活动代码:
- 您使用唯一标识符com.tutsplus.iOS-9-Search.displayShow创建一个新的
NSUserActivity
对象。 入门项目已配置为使用此标识符,因此请确保保留该标识符不变。 - 然后,您将
userInfo
字典分配给用户活动。 稍后将使用它来还原应用程序的状态。 - 您为活动的
title
属性提供一个字符串值。 这就是Spotlight搜索结果中显示的内容。 - 为了确保不仅可以通过标题来搜索内容,还可以提供一组关键字。 在上面的代码片段中,关键字集包括节目名称的每个单词及其类型。
- 接下来,设置
NSUserActivity
对象的许多属性,以告诉操作系统您希望将此用户活动用于什么。 在本教程中,我们仅查看API的搜索组件,因此我们禁用Handoff并启用search 。 - 最后,您可以在用户活动上调用
becomeCurrent
方法,此时该方法会自动添加到设备的搜索结果索引中。
在上面的实现中,您可能注意到注释中的两行。 尽管在本教程中我们将不使用这些属性,但是了解每个属性的用途很重要。
- 通过上述实现,仅在打开应用程序后,才为每个单独的节目创建用户活动和搜索结果。 当您使用户活动
eligibleForPublicIndexing
,Apple开始从用户的搜索结果监视此特定活动的使用和交互。 如果搜索结果吸引了许多用户,Apple会将用户活动提升为自己的云索引 。 一旦用户活动进入该云索引,安装了应用程序的任何人都可以搜索该活动,无论他们是否打开了该特定内容。 仅对于应用程序的所有用户均可访问的活动,才应将此属性设置为true
。 - 用户活动也可以具有可选的
expirationDate
。 设置此属性后,您的用户活动只会显示在指定日期之前的搜索结果中。
既然您知道如何创建一个能够在Spotlight中显示搜索结果的NSUserActivity
,您就可以对其进行测试了。 构建并运行您的应用程序,并在您的应用程序中打开一些节目。 完成此操作后,返回主屏幕(在iOS Simulator中按Command-Shift-H键 )并向下滑动或滚动到最左侧的屏幕以调出搜索视图。
开始输入您打开的节目之一的标题,您将看到它显示在搜索结果中,如下所示。
![开始输入其中一部节目的标题](https://i-blog.csdnimg.cn/blog_migrate/ac69c1164933a4246a82dfdc7a26b7da.png)
或者,输入您打开的节目之一的类型。 由于您已为用户活动分配了关键字,因此这也将导致该节目在搜索结果中列出。
![输入其中一场演出的类型](https://cms-assets.tutsplus.com/uploads/users/855/posts/24375/image/Simulator%20Screen%20Shot%2010%20Jul%202015,%2012.10.10%20PM.png)
您的应用程序的内容已由操作系统正确索引,结果显示在Spotlight中。 但是,当您点击搜索结果时,您的应用程序不会将用户带到相应的搜索结果。 它仅启动应用程序。
幸运的是,与Handoff一样 ,您可以使用NSUserActivity
类在应用程序中还原正确的状态。 为了完成这项工作,我们需要实现两种方法。
如下所示,在AppDelegate
类中实现application(_:continueUserActivity:restorationHandler:)
方法。
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
let splitController = self.window?.rootViewController as! UISplitViewController
let navigationController = splitController.viewControllers.first as! UINavigationController
navigationController.topViewController?.restoreUserActivityState(userActivity)
return true
}
接下来,在MasterViewController
类中实现restoreUserActivityState(_:)
方法。
override func restoreUserActivityState(activity: NSUserActivity) {
if let name = activity.userInfo?["name"] as? String,
let genre = activity.userInfo?["genre"] as? String,
let time = activity.userInfo?["time"] as? NSDate {
let show = Show(name: name, genre: genre, time: time)
self.showToRestore = show
self.performSegueWithIdentifier("showDetail", sender: self)
}
else {
let alert = UIAlertController(title: "Error", message: "Error retrieving information from userInfo:\n\(activity.userInfo)", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: .Cancel, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
在撰写本文时,最新版本的Xcode 7(Beta 3)包含一个问题,正在还原的用户活动的userInfo
属性可以为空。 这就是为什么我要处理任何错误并显示由操作系统返回的userInfo
警报的原因。
再次构建并运行您的应用,然后搜索节目。 当您在搜索结果中点击一个节目时,该应用程序应将您直接带到详细信息视图控制器,并显示您所点击节目的当前信息。
![应用程序已正确还原](https://i-blog.csdnimg.cn/blog_migrate/3667c9e74cb787ddaecce33234a1ac06.png)
2.使用Core Spotlight框架
iOS 9中提供的另一组API可以使您的内容可供用户搜索,这是Core Spotlight框架。 该框架采用数据库风格的设计,可让您提供有关要搜索的内容的更多信息。
在使用Core Spotlight框架之前,我们需要将项目与框架链接。 在“ 项目浏览器”中 ,选择项目并打开顶部的“ 构建阶段”选项卡。 接下来,展开“ 使用库链接二进制文件”部分,然后单击加号按钮。 在出现的菜单中,搜索CoreSpotlight并将您的项目链接到框架。 对MobileCoreServices框架重复这些步骤。
![添加CoreSpotlight框架](https://i-blog.csdnimg.cn/blog_migrate/eefa4d2a212f8428d5b7b239fe90bb1f.png)
接下来,要确保我们的应用提供的搜索结果来自Core Spotlight,请从测试设备或iOS模拟器中删除您的应用,并在DetailViewController
类中注释掉以下行:
activity.becomeCurrent()
最后,打开MasterViewController.swift并在Show
结构定义之前添加以下行:
import CoreSpotlight
import MobileCoreServices
接下来,将以下代码添加到MasterViewController
类的viewDidLoad
方法中:
var searchableItems: [CSSearchableItem] = []
for show in objects {
let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String)
attributeSet.title = show.name
let dateFormatter = NSDateFormatter()
dateFormatter.timeStyle = .ShortStyle
attributeSet.contentDescription = show.genre + "\n" + dateFormatter.stringFromDate(show.time)
var keywords = show.name.componentsSeparatedByString(" ")
keywords.append(show.genre)
attributeSet.keywords = keywords
let item = CSSearchableItem(uniqueIdentifier: show.name, domainIdentifier: "tv-shows", attributeSet: attributeSet)
searchableItems.append(item)
}
CSSearchableIndex.defaultSearchableIndex().indexSearchableItems(searchableItems) { (error) -> Void in
if error != nil {
print(error?.localizedDescription)
}
else {
// Items were indexed successfully
}
}
在测试此代码之前,让我们遍历for
循环的每个步骤。
- 您创建一个
CSSearchableItemAttributeSet
对象,并传递该项目的内容类型 。 例如,如果搜索结果链接到照片,则将传入kUTTypeImage
常量。 - 您将节目的名称分配给属性集的
title
属性。 就像NSUserActivity
,该标题将出现在搜索结果的顶部。 - 接下来,创建一个描述性字符串,并将其分配给可搜索属性集的
contentDescription
属性。 该字符串将显示在Spotlight中结果标题的下方。 - 与使用
NSUserActivity
一样,您可以在搜索结果中创建一组关键字。 - 最后,您创建一个
CSSearchableItem
,它具有唯一的项目标识符,将项目分组在一起的唯一的域标识符以及属性集。 与NSUserActivity
不同,后者从搜索结果返回用户活动,当用户选择搜索结果时,用于CSSearchableItem
的唯一标识符是从操作系统接收到的唯一信息。 您需要使用这些标识符将您的应用还原到正确的状态。
为电视节目创建CSSearchableItem
,您可以使用默认CSSearchableIndex
对象上的indexSearchableItems(_:completionHandler:)
方法对它们进行索引。
生成并运行您的应用程序,您的所有节目都将被Spotlight索引。 导航到搜索视图并搜索其中一个节目。
![CoreSpotlight搜索结果](https://i-blog.csdnimg.cn/blog_migrate/88270dbdf1be7e700a0caede9466f4f5.png)
Spotlight核心搜索结果的处理方法与NSUserActivity
中的处理方法相同,但过程略有不同。 从搜索结果中选择CSSearchableItem
,系统会为您创建一个NSUserActivity
对象,该对象包含所选项目的唯一标识符。
在您的应用程序委托的application(_:continueUserActivity:restorationHandler:)
方法中,可以使用以下实现从Core Spotlight搜索结果中检索所需的信息:
if userActivity.activityType == CSSearchableItemActionType {
if let identifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
// Use identifier to display the correct content for this search result
return true
}
}
使用Core Spotlight框架为应用程序中的内容编制索引时,一个好的做法是在不再需要它们时也删除它们。 CSSearchableIndex
类提供了三种删除可搜索项的方法:
-
deleteAllSearchableItemsWithCompletionHandler(_:)
-
deleteSearchableItemsWithDomainIdentifiers(_:completionHandler:)
-
deleteSearchableItemsWithIdentifiers(_:completionHandler:)
例如,将以下代码添加到MasterViewController
类的viewDidLoad
方法的末尾:
CSSearchableIndex.defaultSearchableIndex().deleteSearchableItemsWithDomainIdentifiers(["tv-shows"]) { (error) -> Void in
if error != nil {
print(error?.localizedDescription)
}
else {
// Items were deleted successfully
}
}
再次构建和运行您的应用程序。 当您尝试搜索任何节目时,不会返回任何结果,因为它们已从索引中删除。
3.结合NSUserActivity
和Core Spotlight
iOS 9中NSUserActivity
类的另一个新添加是contentAttributeSet
属性。 该属性使您可以分配CSSearchableItemAttributeSet
,就像之前创建的一样。 通过此属性集, NSUserActivity
对象的搜索结果可以显示与Core Spotlight搜索结果相同的详细信息。
首先在DetailViewController.swift的顶部添加以下导入:
import CoreSpotlight
import MobileCoreServices
接下来,使用以下实现更新DetailViewController
类中的configureView
方法:
func configureView() {
// Update the user interface for the detail item.
if self.nameLabel != nil && self.detailItem != nil {
self.nameLabel.text = detailItem.name
self.genreLabel.text = detailItem.genre
let dateFormatter = NSDateFormatter()
dateFormatter.timeStyle = .ShortStyle
self.timeLabel.text = dateFormatter.stringFromDate(detailItem.time)
let activity = NSUserActivity(activityType: "com.tutsplus.iOS-9-Search.displayShow")
activity.userInfo = ["name": detailItem.name, "genre": detailItem.genre, "time": detailItem.time]
activity.title = detailItem.name
var keywords = detailItem.name.componentsSeparatedByString(" ")
keywords.append(detailItem.genre)
activity.keywords = Set(keywords)
activity.eligibleForHandoff = false
activity.eligibleForSearch = true
//activity.eligibleForPublicIndexing = true
//activity.expirationDate = NSDate()
let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String)
attributeSet.title = detailItem.name
attributeSet.contentDescription = detailItem.genre + "\n" + dateFormatter.stringFromDate(detailItem.time)
activity.becomeCurrent()
}
}
最后一次构建并运行您的应用,然后打开一些节目。 现在搜索节目时,您将看到使用NSUserActivity
创建的结果包含与Core Spotlight搜索结果相同的详细程度。
![NSUserActivity结果与CoreSpotlight详细信息](https://cms-assets.tutsplus.com/uploads/users/855/posts/24375/image/Simulator%20Screen%20Shot%2010%20Jul%202015,%205.58.56%20PM.png)
结论
在本教程中,您学习了如何使用NSUserActivity
类和Core Spotlight框架通过iOS Spotlight访问应用程序的内容。 我还向您展示了如何使用这两个API来索引应用程序中的内容,以及当用户选择搜索结果时如何恢复应用程序的状态。
iOS 9引入的新搜索API非常易于使用,并使应用程序的内容更易于发现,并且对应用程序的用户更易于访问。 与往常一样,如果您有任何意见或问题,请将其保留在下面的评论中。
翻译自: https://code.tutsplus.com/tutorials/ios-9-introducing-search-apis--cms-24375