iOS--系统相册分享照片、链接到自己的APP(扩展)

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

选取照片

 2、系统会弹出控制面板,这里你会发现有微信APP,如果没有可以点击更多按钮开启微信就行

拉起控制面板

 3、点击微信,进入下面页面,在这里你可以进行自己想要的操作,比如发送图片给好友

WX20190522-114017.png

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

创建扩展Target

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

选择Share Extension

起扩展名

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

Activate

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

扩展

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


选择创建的扩展

Choose an app to 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,并设置一个限制数量,如图

设置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”如图:

激活App Groups

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


容器APP添加

分享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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342