iOS8之后创建Action Extension应用扩展

对于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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值