相信很多人发现,苹果手机有个功能就是可以从相册选择照片分享到相应的APP,比如将照片分享到微信好友,操作如下:
1、先选取照片,然后点击分享按钮:
2、系统会弹出控制面板,这里你会发现有微信APP,如果没有可以点击更多按钮开启微信就行
3、点击微信,进入下面页面,在这里你可以进行自己想要的操作,比如发送图片给好友
那我们怎么将自己的APP添加到这个控制面板上呢?然后方便用户的操作?首先这个功能属于App Extension
应用扩展功能,想要此功能,首先创建扩展Target
选择Share Extension
,然后起个扩展名,最后Finish:
这时候会提示创建一个Scheme,点击
Activate
这时候你会发现项目多个一个ImagePublish
扩展
这时创建Extension完成,我们进行下测试,选择创建的扩展,运行,XCode中会弹出界面让我们选择一个应用来运行Extension,我们选择照片,点击Run,如图:
这时手机会打开应用相册,选择一张照片,点击分享,这时你会发现,你的应用出现在控制面板当中了,这时说明我们创建成功了。点击我们自己的应用,会弹出一个框,如下图:
为什么会出现这个框呢?其实这时系统的模板,就是说你创建扩展的时候系统自带的样式,这时候我们就要对扩展进行一些设置了,打开扩展Info.plist
文件
我们需要设置哪些东西,首先了解这些字段的意思:
字段 | 说明 |
---|---|
Bundle display name | 扩展的显示名称,默认跟你的项目名称相同,可以通过修改此字段来控制扩展的显示名称 |
NSExtension | 扩展描述字段,用于描述扩展的属性、设置等 |
NSExtensionAttributes | 扩展属性集合字段,用于描述扩展的属性 |
NSExtensionActivationRule | 激活扩展的规则。默认为字符串“TRUEPREDICATE”,表示在分享菜单中一直显示该扩展。可以将类型改为Dictionary类型,然后添加以下字段NSExtensionActivationSupportsAttachmentsWithMaxCount(附件最多限制。附件包括File、Image和Movie三大类,单一、混选总量不超过指定数量)、NSExtensionActivationSupportsAttachmentsWithMinCount(附件最少限制,默认至少选择1个附件,分享菜单中才显示扩展插件图标)、NSExtensionActivationSupportsImageWithMaxCount(图片最多限制,超过不显示)、NSExtensionActivationSupportsMovieWithMaxCount(视频最多限制。单一、混选均不超过指定数量)、NSExtensionActivationSupportsWebPageWithMaxCount(Web页面最多限制。默认不支持Web页面分享,需要自己设置一个数值)、NSExtensionActivationSupportsWebURLWithMaxCount(Web链接最多限制。默认不支持分享超链接,需要自己设置一个数值)、NSExtensionActivationSupportsFileWithMaxCount(文件最多限制,为数值类型。文件泛指除Image/Movie之外的附件,例如【邮件】附件、【语音备忘录】等)、NSExtensionActivationSupportsText(是否支持文本类型,布尔类型,默认不支持。如【备忘录】的分享) |
NSExtensionMainStoryboard | 设置主界面的Storyboard,系统自带模板 |
NSExtensionPointIdentifier | 扩展标识,在分享扩展中为:com.apple.share-services |
NSExtensionPrincipalClass sionAttributes | 自定义UI的类名,当不想用系统自带模板可设置此参数,指定自定义UIViewController子类名 |
对于不同的应用里面有可能出现只允许接受某种类型的内容,那么扩展就不能一直出现在分享菜单中,因为不同的应用提供的分享内容不一样,这就需要通过设置NSExtensionActivationRule字段来决定Share Extension是否显示。例如,只想接受其他应用图片到自己的应用,那么可以通过下面的步骤来设置:
将NSExtensionActivationRule字段类型由String改为Dictionary。
展开NSExtensionActivationRule字段,创建其子项NSExtensionActivationSupportsImageWithMaxCount,并设置一个限制数量,如图
这是就是当出现图片且小于9张时,应用就会出现在控制面板中,点击应用弹出弹框时,有个取消和发布按钮,我们怎么获取点击事件呢?在用系统模板中,我们发现有一个叫ShareViewController
的控制器,它继承SLComposeServiceViewController
,.m
文件有几个方法,就是点击事件触发的方法,输入框输入的内容我们也能够获取
当用户点击提交按钮的时候,扩展要做的事情就是要把数据取出来,比如图片,并且放入一个与Containing App共享的数据介质中(包括NSUserDefault、Sqlite、CoreData),因为尽管苹果开放了Extension,但是在iOS中Extension并不能单独存在,要想提交到AppStore,必须将Extension包含在一个App中提交,并且App的实现部分不能为空,这个包含Extension的App就叫Containing app,也就是宿主APP。Extension会随着宿主APP的安装而安装,同时随着宿主APP的卸载而卸载。要跟宿主APP进行数据交互需要借助AppGroups服务。怎么获取扩展中的数据呢?
在Extension中,UIViewController包含一个extensionContext这样的上下文对象,可通过该对象获取分享的内容,具体代码如下:
- (void)didSelectPost {
NSLog(@"点击发布");
//获取文本内容
NSString *textString = self.textView.text;
self.imageDataArray = [NSMutableArray array];
//扩展中的处理不能太长时间阻塞主线程,放入线程中处处理,否则可能导致苹果拒绝你的应用
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSExtensionItem *item in self.extensionContext.inputItems) {
NSInteger count = item.attachments.count;
for (NSItemProvider *itemProvider in item.attachments) {
if ([itemProvider hasItemConformingToTypeIdentifier:@"public.image"]) {
//获取缩略图,但备忘录获取不到 item UIImage类型
[itemProvider loadPreviewImageWithOptions:nil completionHandler:^(id<NSSecureCoding> _Nullable item, NSError * _Null_unspecified error) {
}];
//item Url类型:file:///var/mobile/Media/PhotoData/OutgoingTemp/0F2F2637-0DBF-44F2-8F89-EFD9579BB76E/RenderedPhoto/IMG_0185.JPG
[itemProvider loadItemForTypeIdentifier:@"public.image" options:nil completionHandler:^(id<NSSecureCoding> _Nullable item, NSError * _Null_unspecified error) {
// 对itemProvider夹带着的图片进行解析
NSURL *imageUrl = (NSURL *)item;
// 把图片转换为data数据
NSData *data = [NSData dataWithContentsOfURL:imageUrl];
[self.imageDataArray addObject:data];
dispatch_async(dispatch_get_main_queue(), ^{
if (self.imageDataArray.count == count) {
NSLog(@"%@", [NSString stringWithFormat:@"获取全部%ld张照片",(long)count]);
//获取全部再销毁
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
}
});
}];
}
}
}
});
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
}
通过以上操作,我们获取了照片等信息(分享链接也是同样操作,设置info.plist
,和获取的时候改为public.url
就行),接下来就是将分享数据传递给容器程序,我们知道我们的应用和扩展是两个独立的TARGETS
,在默认情况下,iOS的应用是存在一个沙盒里面的,不允许应用与应用直接进行数据的交互。为此,苹果提供了一项叫App Groups的服务,该服务允许开发者可以在自己的应用之间通过NSUserDefaults、NSFileManager或者CoreData来进行相互的数据传输,接下来介绍怎么开通App Groups服务。
首先,到开发者中心为创建的扩展工程添加App ID,操作步骤和普通项目创建App ID一样,在APP Services勾选App Groups,然后分别打开容器应用和分享应用的项目配置的Capabilities页签,激活App Groups特性,点击+
添加App groups ,命名格式为:“group.+bundle identifier”如图:
其实现在现在工程都是automatically manage signing,直接配置Capabilities页签,开启App Groups后自动生成App ID。经过上图操作,容器程序的App Groups已经设置完成,接下来激活扩展应用App Groups服务,进入扩展Capabilities页签,你会发现不需要创建新的App Group,只需要打钩就行,这些操作其实就是实现两个APP数据共享:
添加成功后,会发现两个项目多了一个东西:
到这里应用和扩展的App Groups服务都已经启动,接下来就是要进行数据传输了,上面说到,我们可以用NSUserDefaults、NSFileManager以及CoreData三种方式进行传输,在这里本人使用NSUserDefaults方法进行数据传输,因为个人觉得更简单,但需要注意的是,要想设置或访问Group的数据,不能在使用standardUserDefaults方法来获取一个NSUserDefaults对象了。应该使用initWithSuiteName:
方法来初始化一个NSUserDefaults对象,其中的SuiteName就是创建的Group的名字,然后利用这个对象来实现,跨应用的数据读写:
//name同App groups匹配
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.jhj.Share.ImagePublish"];
//存图片数组
[userDefaults setObject:self.imageDataArray forKey:@"shareImageDataArray"];
//用于标记是新的分享
[userDefaults setBool:YES forKey:@"newShare"];
//存文本内容
[userDefaults setObject:textString forKey:@"shareTextString"];
存储成功后,最后就是在容器APP获取分享数据,在容器APP中Appdelegate中applicationDidBecomeActive
实现获取,代码如下:
- (void)applicationDidBecomeActive:(UIApplication *)application {
//获取共享的UserDefaults
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.jhj.Share.ImagePublish"];
if ([userDefaults boolForKey:@"newShare"]){
NSArray *imagesDataArray = [userDefaults valueForKey:@"shareImageDataArray"];
NSLog(@"新的分享 : %lu", (unsigned long)imagesDataArray.count);
//重置分享标识
[userDefaults setBool:NO forKey:@"newShare"];
//自己相应的操作,比如请求服务器
}
}
这样就基本实现了应用扩展和数据共享了!
注意:
1、有的时候扩展中的一些功能和宿主APP的某些功能很类似,但是又不能直接引用,下面提供两种解决办法:
1)复制一份代码到扩展应用里,这种办法傻瓜式,但不容易出错;
2)直接打开需要共享使用类的.m文件,你想使用哪个文件就勾选哪个,这样就可以,如下:
但这样有一个问题,如果共享的文件又包含其他文件,则得到其他文件继续2操作,不然会报错;
2、在扩展中用到的第三方库怎么办?在项目Podfile文件中,添加target 'ImagePublish' do
,记得加end
,然后和普通工程一样添加需要的库,回终端执行pod install
命令,就给扩展安装了第三方库,如下:
3、扩展中的处理不能太长时间阻塞主线程,可以放入线程中处理,否则可能导致苹果拒绝你的应用;
4、扩展不能单独提交审核,必须要跟容器APP一起提交进行审核;
5、扩展APP和容器APP的Build Version要保持一致,否则在上传审核包的时候会提示警告,导致程序无法正常提审;
声明: 转载请注明出处https://www.jianshu.com/p/67ed14b1cee1