iOS8之后创建Action Extension应用扩展

来源:互联网 发布:hr人力资源软件免费 编辑:程序博客网 时间:2024/05/02 02:26

对于Action Extension,可能是扩展性最高Extension了,试想我们可以在其他应用中将信息通过Action Extension传递譬如选中的文字到另一个APP,是不是很爽,举个例子:
这里写图片描述
这里写图片描述
当点击Activity中的NoteAppExtension,将选中的文字传递到NoteAppExtension;

⭐️我们开始吧
File > New > Project菜单创建一个新的工程,选择Single View Application:

然后通过File > New > Target菜单给给工程添加一个Target,选择Action Extension:

在创建Action扩展时需要指定一个Aciton类型,Apple提供了两种Action扩展的类型模板。一种是有用户界面的类型,包含一个UIViewController和一个Storeboard文件,可以自定义显示界面和行为。另一种是不带用户界面的类型,这种类型只允许我们处理来自Host应用的请求。

⭐️一:这里我们选择”No User Interface”

现在我们在工程中就可以看到刚才创建的Action扩展NoteAppExtension,它包含两个主要的文件,一个是Action.js,另一个是ActionRequestHandler.swift;

我们来看看这两个文件的作用。Action.js文件用来实现和处理浏览器中请求的逻辑,在本文的例子中,它主要实现用户在浏览器中选中文本并发送到我们的应用中。ActionRequestHandler.swift用来处理Host应用发送的请求和参数。

在实现逻辑之前我们需要设置一下扩展的属性,打开Info.plist文件将NSExtension>NSExtensionAttributes>NSExtensionActivationSupportsWebURLWithMaxCount属性设置为1,该设置让扩展知道我们需要请求一个URL。

我们Action.js文件中有如下内容:

var Action = function() {};Action.prototype = {    run: function(arguments) {        // 在这个方法里,你可以通过document操作HTML中的元素,或者可以将HTML中的内容传给ActionRequestHandler文件的代码。        // 在本文的例子中,我们不做任何更新,只是将HTML中选中的内容穿给ActionRequestHandler文件的代码。        var selected = "No Text Selected";        if (window.getSelection) {            selected = window.getSelection().getRangeAt(0).toString();        } else {            selected = document.getSelection().getRangeAt(0).toString();        }        arguments.completionFunction({"args" : selected});    },    finalize: function(arguments) {        // 当ActionRequestHandler文件中的itemLoadCompletedWithPreprocessingResults方法执行完之后会调用该方法。        // 如果ActionRequestHandler文件向HTML返回了信息,我们可以通过arguments["message"]来查看,并且可以根据该信息操作HTML中的元素。        alert(arguments["message"])    }};var ExtensionPreprocessingJS = new Action

Safari与Action扩展的交互就是通过Action.js文件中的run和finalize这两个方法实现的。当我们在Safari中使用Action扩展时就会调用run方法,它能让我们在该方法中操作当前Safari显示页面的DOM元素。当Action扩展处理完逻辑向Safari返回信息时会调用finalize方法,在我们的例子中,我们通过self.extensionContext!.completeRequestReturningItems(nil, completionHandler: nil)这段代码向Safari返回信息。该方法的第一个参数就是要返回的信息,它会将信息传给Action.js文件,然后通过js代码操作HTML。如果第一个参数传入nil,那就意味着不会调用Action.js文件中的finalize方法。run和finalize这两个方法的参数arguments都包含着一些信息,只不过一个是来自与HTML,一个来自ActionRequestHandler文件。

一定要记住:我们必须要实例化ExtensionPreprocessingJS这个全局变量,因为它是Safari和Action扩展之间的桥梁。

我们的ActionRequestHandler文件内容如下:

class ActionRequestHandler: NSObject, NSExtensionRequestHandling {    var extensionContext: NSExtensionContext?    func beginRequestWithExtensionContext(context: NSExtensionContext!) {        self.extensionContext = context        let identifierType = NSString(format: kUTTypePropertyList, NSUTF8StringEncoding)        for (item: NSExtensionItem) in context.inputItems as [NSExtensionItem] {            for (itemProvider: NSItemProvider) in item.attachments as [NSItemProvider] {                if itemProvider.hasItemConformingToTypeIdentifier(identifierType) {                    itemProvider.loadItemForTypeIdentifier(identifierType, options: nil, completionHandler: {(item, error) in                        let dictionary = item as NSDictionary                        dispatch_async(dispatch_get_main_queue(), {                            self.itemLoadCompletedWithPreprocessingResults(dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as NSDictionary)                        })                    })                }            }        }    }    func itemLoadCompletedWithPreprocessingResults(javaScriptPreprocessingResults: NSDictionary) {        if let text = javaScriptPreprocessingResults["args"] as? String {            let userDefaults = NSUserDefaults(suiteName: "group.name")            userDefaults.setValue(text, forKey: "note")            userDefaults.synchronize()            self.doneWithResults(["message": "Successfully added to the note app"])        }    }    func doneWithResults(resultsForJavaScriptFinalizeArg: NSDictionary?) {        if let resultsForJavaScriptFinalize = resultsForJavaScriptFinalizeArg {            let identifierType = NSString(format: kUTTypePropertyList, NSUTF8StringEncoding)            // 创建合适返回类型的标示符。                        // 这里创建的resultsItem将作为Action.js文件中finalize方法的参数。            var resultsDictionary = [NSExtensionJavaScriptFinalizeArgumentKey: resultsForJavaScriptFinalize]            var resultsProvider = NSItemProvider(item: resultsDictionary, typeIdentifier: identifierType)            var resultsItem = NSExtensionItem()            resultsItem.attachments = [resultsProvider]            // 这段代码意味着Action扩展已经处理完了逻辑,现在将信息返回给Action.js文件。            self.extensionContext!.completeRequestReturningItems([resultsItem], completionHandler: nil)        } else {            // 就算我们没有任何要返回的信息,也要执行这段代码,用于告知我们的Action扩展已经完成了逻辑处理。            self.extensionContext!.completeRequestReturningItems(nil, completionHandler: nil)        }        self.extensionContext = nil    }}

当我们在safari中选择一段文字后,点击分享按钮,先添加NoteAppExtension,点击NoteAppExtension图标,效果就出来了

⭐️二:选择Presents User Interface
上面是对Safari的扩展,那么这个就是对任意APP的了,生产的Extension会有三个文件,ActionViewController.swift、MainInterface.storyboard、info.plist;
下面示范一个使用UIActivityViewController传值(这里以文字举例),在当前程序的Action Extension中接收的例子:

在Action Extension的MainInterface.storyboard中,添加一个UITextView控件,用来接收文字,如图:

接下来,在主target的ViewController.swift中,设置一个button,并在点击事件中:`@IBAction func qw(sender: AnyObject) {

let textToShare = "请大家登录《iOS云端与网络通讯》服务网站。"let activityItems:[AnyObject] = [textToShare]let activityVC = UIActivityViewController.init(activityItems: activityItems, applicationActivities: nil)//不出现在活动项目activityVC.excludedActivityTypes = [UIActivityTypePrint, UIActivityTypeCopyToPasteboard, UIActivityTypeAssignToContact,UIActivityTypeSaveToCameraRoll, UIActivityTypeAddToReadingList]self.presentViewController(activityVC, animated:true) {}

}`

在Extension的ActionViewController.swift的viewDidLoad中:

let item = self.extensionContext!.inputItems[0];      let itemProvider = item.attachments!![0]      if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeText as String) {        itemProvider.loadItemForTypeIdentifier(kUTTypeText as String, options: nil, completionHandler: { (item, error) in          print("------\(item)")          // 主线程中刷新UI          dispatch_async(dispatch_get_main_queue(), {             self.texts.text = item as! String            self.content.text = item as? String          })        })      }

代码到这里就写完了,如果想要其他的功能,可以自己百度了

下面是一个需要注意的设置:
我们还需要为Action扩展指定所能支持的数据类型。在这一次的实例当中,我们只需要支持纯文本数据即可。展开Supporting Files组并选择Info.plist。在Info.plist当中,遵循NSExtension > NSExtensionAttributes > NSExtensionActivationRule导航流程,最后将NSExtensionActivationRule的类型由String变更为Dictionary。

在已经展开的dictionary当中,点击旁边的+号按钮,从而添加一个新的子级键。将其名称设置为NSExtensionActivationSupportsText,类型设定成Boolean,而值则取为YES。这样一来,我们就能确保自己的Action扩展只在输入项目包含文本内容时才会显示出来。

仍然是在Info.plist当中,我们要将Bundle Display Name变更为Read It。这样看起来会更加清晰。

作为画龙点睛之笔,大家可以为Action扩展添加一个图标。在Project Navigator当中,选择该项目并在目标之下选定ReadItAction目标。在App Icons and Launch Images部分中的General标签下点击App Icons Source旁边的Use Asset Catalog。根据提示,我们接下来需要点击Migrate。而后慎用至资产目录并将以下图标拖拽至iPhone App iOS 7,8 60pt 2x位置。

完成应用程序的构建与运行步骤,看看一切是否像我们预期的那样运转正常。不过还有一个需要关注的问题:如果声音图标没有被正常显示在Action扩展之内,大家需要确保主Images.xcassets文件已经被正确复制到了扩展目标当中。

要完成这项操作,我们需要在Project Navigator当中选定该项目并从Targets列表当中选择ReadItAction目标。打开屏幕顶端的Build Phases标签并展开Copy Bundle Resources步骤。如果Images.xcassets文件并未出现在资源列表当中,那么点击其中的小加号将其手动添加到列表之内。

OK,这两个例子只是特定的应用场景,其实还有很多扩展,具体参考文章http://code.tutsplus.com/tutorials/ios-8-how-to-build-a-simple-action-extension–cms-22794

0 0