iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)

出自:https://www.jianshu.com/p/67ed14b1cee1

做这个Demo要有付费的开发者账号



相信很多人发现,苹果手机有个功能就是可以从相册选择照片分享到相应的APP,比如将照片分享到微信好友,操作如下:

1、先选取照片,然后点击分享按钮:
iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第1张图片
选取照片
2、系统会弹出控制面板,这里你会发现有微信APP,如果没有可以点击更多按钮开启微信就行
iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第2张图片
拉起控制面板
3、点击微信,进入下面页面,在这里你可以进行自己想要的操作,比如发送图片给好友
iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第3张图片
发送给微信

那我们怎么将自己的APP添加到这个控制面板上呢?然后方便用户的操作?首先这个功能属于App Extension应用扩展功能,想要此功能,首先创建扩展Target

iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第4张图片
创建扩展Target

选择Share Extension,然后起个扩展名,最后Finish:

iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第5张图片
选择Share Extension
iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第6张图片
起扩展名

这时候会提示创建一个Scheme,点击Activate

iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第7张图片
Activate

这时候你会发现项目多个一个ImagePublish扩展

iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第8张图片
扩展

这时创建Extension完成,我们进行下测试,选择创建的扩展,运行,XCode中会弹出界面让我们选择一个应用来运行Extension,我们选择照片,点击Run,如图:

iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第9张图片
选择创建的扩展
iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第10张图片
Choose an app to run

这时手机会打开应用相册,选择一张照片,点击分享,这时你会发现,你的应用出现在控制面板当中了,这时说明我们创建成功了。点击我们自己的应用,会弹出一个框,如下图:

iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第11张图片
弹框

为什么会出现这个框呢?其实这时系统的模板,就是说你创建扩展的时候系统自带的样式,这时候我们就要对扩展进行一些设置了,打开扩展Info.plist文件

iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第12张图片
系统模板

我们需要设置哪些东西,首先了解这些字段的意思:

字段 说明
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,并设置一个限制数量,如图

设置NSExtensionActivationSupportsImageWithMaxCount

这是就是当出现图片且小于9张时,应用就会出现在控制面板中,点击应用弹出弹框时,有个取消和发布按钮,我们怎么获取点击事件呢?在用系统模板中,我们发现有一个叫ShareViewController的控制器,它继承SLComposeServiceViewController.m文件有几个方法,就是点击事件触发的方法,输入框输入的内容我们也能够获取

iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第13张图片
触发事件

  当用户点击提交按钮的时候,扩展要做的事情就是要把数据取出来,比如图片,并且放入一个与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  _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  _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”如图:

iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第14张图片
激活App Groups

  其实现在现在工程都是automatically manage signing,直接配置Capabilities页签,开启App Groups后自动生成App ID。经过上图操作,容器程序的App Groups已经设置完成,接下来激活扩展应用App Groups服务,进入扩展Capabilities页签,你会发现不需要创建新的App Group,只需要打钩就行,这些操作其实就是实现两个APP数据共享:

iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第15张图片
容器APP添加
iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第16张图片
分享APP添加

添加成功后,会发现两个项目多了一个东西:

iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第17张图片
多了两个

  到这里应用和扩展的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文件,你想使用哪个文件就勾选哪个,这样就可以,如下:

iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第18张图片
共享

  但这样有一个问题,如果共享的文件又包含其他文件,则得到其他文件继续2操作,不然会报错;
  2、在扩展中用到的第三方库怎么办?在项目Podfile文件中,添加target 'ImagePublish' do,记得加end,然后和普通工程一样添加需要的库,回终端执行pod install命令,就给扩展安装了第三方库,如下:

iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号)_第19张图片
第三方库

  3、扩展中的处理不能太长时间阻塞主线程,可以放入线程中处理,否则可能导致苹果拒绝你的应用;
  4、扩展不能单独提交审核,必须要跟容器APP一起提交进行审核;
  5、扩展APP和容器APP的Build Version要保持一致,否则在上传审核包的时候会提示警告,导致程序无法正常提审;

你可能感兴趣的:(iOS--系统相册分享照片、链接到自己的APP(扩展,需要付费账号))