然后选择”iOS” -> “Application Extension” -> “Share Extension”,点击“Next”。如图: 给扩展起个名字,这里填写了“ShareExtension”,确定你的目标Target,点击“Finish”。
4、创建后工程目录会出现ShareExtension的文件夹。
5、这时候可以选择host app
编译运行,看看效果。
这里选择相册做为宿主app。
编译成功后选择一张图片进行分享,d出系统分享面板,发现没有出现你的应该App,别着急,检查一下。
可能是你的扩展不支持该分享的文件类型
你需要在扩展的
info.plist
文件添规则。
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsAttachmentsWithMaxCount</key>
<string>9</string>
<key>NSExtensionActivationSupportsAttachmentsWithMinCount</key>
<string>1</string>
<key>NSExtensionActivationSupportsFileWithMaxCount</key>
<string>1</string>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>9</integer>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
看出现了:
点击app出现默认的界面:
刚创建的扩展中的info.plist
内容配置是这样的:
NSExtension
: 扩展描述字段,用于描述扩展的属性、设置等。作为一个扩展项目必须要包含此字段。
NSExtensionAttributes
: 扩展属性集合字段。用于描述扩展的属性。
NSExtensionMainStoryboard
: 设置主界面的Storyboard,如果不想使用storyboard,也可以使用NSExtensionPrincipalClass指定自定义UIViewController子类名
NSExtensionPointIdentifier
: 扩展标识,在分享扩展中为:com.apple.share-services。
NSExtensionPrincipalClass
: 自定义UI的控制器名称。
NSExtensionActivationSupportsAttachmentsWithMaxCount
: 附件最多限制,为数值类型。附件包括File、Image和Movie三大类,单一、混选总量不超过指定数量。
NSExtensionActivationSupportsAttachmentsWithMinCount
: 附件最少限制,为数值类型。当设置NSExtensionActivationSupportsAttachmentsWithMaxCount时生效,默认至少选择1个附件,分享菜单中才显示扩展插件图标。
NSExtensionActivationSupportsFileWithMaxCount
: 文件最多限制,为数值类型。文件泛指除Image/Movie之外的附件,例如【邮件】附件、【语音备忘录】等。单一、混选均不超过指定数量。
NSExtensionActivationSupportsImageWithMaxCount
: 图片最多限制,为数值类型。单一、混选均不超过指定数量。
NSExtensionActivationSupportsMovieWithMaxCount
: 视频最多限制,为数值类型。单一、混选均不超过指定数量
NSExtensionActivationSupportsText
: 是否支持文本类型,布尔类型,默认不支持。如【备忘录】的分享
NSExtensionActivationSupportsWebURLWithMaxCount
: Web链接最多限制,为数值类型。默认不支持分享超链接,需要自己设置一个数值。
NSExtensionActivationSupportsWebPageWithMaxCount
: Web页面最多限制,为数值类型。默认不支持Web页面分享,需要自己设置一个数值。
对于不同的应用里面有可能出现只允许接受某种类型的内容,那么Share Extension就不能一直出现在分享菜单中,因为不同的应用提供的分享内容不一样,这就需要通过设置NSExtensionActivationRule
字段来决定Share Extension是否显示。
具体可查看官方文档App Extension Keys
分析ShareViewControllerShareViewController
继承SLComposeServiceViewController
里面默认配置了三个方法:
isContentValid
: 来判断我们获取到得数据是否是我们想要的.
didSelectPost
: 是惦记post按钮后触发选择的方法。
configurationItems
:返回一个配置项数组,会在默认界面底下添加一个tableView列表,可以进行点击选择,也可以在跳转到其他页面选择内容。
例如:
- (NSArray *)configurationItems {
// To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
__weak typeof(self) weakSelf = self;
SLComposeSheetConfigurationItem *item1 = [[SLComposeSheetConfigurationItem alloc] init];
item1.title = @"发送给朋友";
item1.value = @"请选择";
item1.tapHandler = ^{
NSLog(@"发送给朋友");
[self validateContent];
};
SLComposeSheetConfigurationItem *item2 = [[SLComposeSheetConfigurationItem alloc] init];
item2.title = @"发送到朋友圈";
item2.value = @"点我";
item2.tapHandler = ^{
NSLog(@"发送到朋友圈");
ShareActViewController *vc = [ShareActViewController new];
[weakSelf pushConfigurationViewController:vc];
vc.clickBlock = ^(NSString * _Nonnull text) {
[weakSelf popConfigurationViewController];
weakSelf.textView.text = text;
};
};
return @[item1, item2];
}
输入文本后,点击post会触发方法didSelectPost
,通过contentText
属性获取输入的文本,以及self.extensionContext.inputItems
获取分享到文件。
NSLog(@"share 文本:%@", self.contentText);
NSExtensionItem *imageItem = [self.extensionContext.inputItems firstObject];
NSItemProvider *imageItemProvider = [[imageItem attachments] firstObject];
if([imageItemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeFileURL]) {
[imageItemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeFileURL options:nil completionHandler:^(__kindof id<NSSecureCoding> _Nullable item, NSError * _Null_unspecified error) {
NSLog(@"收到的文件 : %@",item);
if ([(NSObject *)item isKindOfClass:[NSURL class]]) {
NSURL *url = (NSURL *)item;
/// 保存文件数据
NSData *data = [NSData dataWithContentsOfURL:url];
[KSuiteUserDefault(kSuiteShareName) setObject:data forKey:kShareFileData];
[KSuiteUserDefault(kSuiteShareName) synchronize];
/// 跳转到容器app
UIResponder* responder = self;
while ((responder = [responder nextResponder]) != nil)
{
NSLog(@"responder = %@", responder);
if([responder respondsToSelector:@selector(openURL:)] == YES)
{
[responder performSelector:@selector(openURL:) withObject:[NSURL URLWithString:[NSString stringWithFormat:@"extensionShare://%@",[url absoluteString]]]];
}
}
}
}];
}
/// 关闭视图界面
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
自定义分享界面
你需要创建一个继承于ViewController
新的控制器,例如:ShareCustomViewController
。文件夹中默认创建的ShareViewController
和storyboard
可以删掉。修改Info.plist
文件的配置。删除掉NSExtensionMainStoryboard
项,添加NSExtensionPrincipalClass
,值是新创建的控制器。先
clean
后再编译一下。编译成功后你就可以自定义UI了。
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor orangeColor];
UIView *container = [[UIView alloc] initWithFrame:CGRectMake((self.view.frame.size.width - 300) / 2, (self.view.frame.size.height - 175) / 2, 300, 175)];
container.layer.cornerRadius = 7;
container.layer.borderColor = [UIColor lightGrayColor].CGColor;
container.layer.borderWidth = 1;
container.layer.masksToBounds = YES;
container.backgroundColor = [UIColor whiteColor];
container.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
[self.view addSubview:container];
//定义Post和Cancel按钮
UIButton *cancelBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[cancelBtn setTitle:@"Cancel" forState:UIControlStateNormal];
cancelBtn.frame = CGRectMake(8, 8, 65, 40);
[cancelBtn addTarget:self action:@selector(cancelBtnClickHandler:) forControlEvents:UIControlEventTouchUpInside];
[container addSubview:cancelBtn];
UIButton *postBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[postBtn setTitle:@"Post" forState:UIControlStateNormal];
postBtn.frame = CGRectMake(container.frame.size.width - 8 - 65, 8, 65, 40);
[postBtn addTarget:self action:@selector(postBtnClickHandler:) forControlEvents:UIControlEventTouchUpInside];
[container addSubview:postBtn];
//定义一个分享链接标签
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(8,
cancelBtn.frame.origin.y + cancelBtn.frame.size.height + 8,
container.frame.size.width - 16,
container.frame.size.height - 16 - cancelBtn.frame.origin.y - cancelBtn.frame.size.height)];
label.numberOfLines = 0;
label.textAlignment = NSTextAlignmentCenter;
[container addSubview:label];
//获取分享链接
__block BOOL hasGetUrl = NO;
[self.extensionContext.inputItems enumerateObjectsUsingBlock:^(NSExtensionItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj.attachments enumerateObjectsUsingBlock:^(NSItemProvider * _Nonnull itemProvider, NSUInteger idx, BOOL * _Nonnull stop) {
if ([itemProvider hasItemConformingToTypeIdentifier:@"public.url"])
{
[itemProvider loadItemForTypeIdentifier:@"public.url" options:nil completionHandler:^(id<NSSecureCoding> _Nullable item, NSError * _Null_unspecified error) {
if ([(NSObject *)item isKindOfClass:[NSURL class]])
{
dispatch_async(dispatch_get_main_queue(), ^{
label.text = ((NSURL *)item).absoluteString;
self.urlString = ((NSURL *)item).absoluteString;
});
}
}];
hasGetUrl = YES;
*stop = YES;
}
*stop = hasGetUrl;
}];
}];
}
- (void)cancelBtnClickHandler:(id)sender
{
//取消分享
[self.extensionContext cancelRequestWithError:[NSError errorWithDomain:@"CustomShareError" code:NSUserCancelledError userInfo:nil]];
}
- (void)postBtnClickHandler:(id)sender
{
UIResponder* responder = self;
while ((responder = [responder nextResponder]) != nil)
{
NSLog(@"responder = %@", responder);
if([responder respondsToSelector:@selector(openURL:)] == YES)
{
// [responder performSelector:@selector(openURL:) withObject:[NSURL URLWithString:@"extensionShare://"]];
[responder performSelector:@selector(openURL:) withObject:[NSURL URLWithString:[NSString stringWithFormat:@"extensionShare://%@",self.urlString]]];
}
}
//执行分享内容处理
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
}
数据共享
数据共享的配置和三种方式,具体参考上一篇文章:
iOS App Extensions初识及工作原理
首先你需要在App里配置UrlScheme
由于扩展中是无法访问sharedApplication
对象,因此不能使用openUrl
进行程序间的跳转,那怎么办呢。
也许你查看了一下self.extensionContext
属性,发现了这个API:
// Asks the host to open a URL on the extension's behalf
- (void)openURL:(NSURL *)URL completionHandler:(void (^ _Nullable)(BOOL success))completionHandler;
然后试了一下发现没有效果,不好意思这个API只对today widget
小组件管用。
那怎么办呢?只能根据响应链获取可以触发openURL:
的对象,执行跳转:
UIResponder* responder = self;
while ((responder = [responder nextResponder]) != nil) {
NSLog(@"responder = %@", responder);
if([responder respondsToSelector:@selector(openURL:)] == YES) {
[responder performSelector:@selector(openURL:) withObject:[NSURL URLWithString:@"extensionShare://"]];
}
}
跳转后触发AppDelegate
中application:openURL:options:
函数,在这里进行数据的处理。
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
NSLog(@"openURL : %@", url);
if ([url.description hasPrefix:@"extensionShare"]) {
NSLog(@"分享跳转");
}
return YES;
}
不显示界面直接跳转到容器App
如果你想点击的时候不显示界面直接跳转到容器App,那么你需要创建一个自定义控制器,然后在viewWillAppear
视图出现的时候接收到文件后跳转并关闭界面。
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSExtensionItem *item = [self.extensionContext.inputItems firstObject];
NSItemProvider *itemProvider = [[item attachments] firstObject];
if([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeData]) {
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeData options:nil completionHandler:^(__kindof id<NSSecureCoding> _Nullable item, NSError * _Null_unspecified error) {
NSLog(@"收到的文件 : %@",item);
if ([(NSObject *)item isKindOfClass:[NSURL class]]) {
NSURL *url = (NSURL *)item;
[self.shareHandler saveFileWithUrl:url];
UIResponder* responder = self;
while ((responder = [responder nextResponder]) != nil)
{
NSLog(@"responder = %@", responder);
if([responder respondsToSelector:@selector(openURL:)] == YES)
{
[responder performSelector:@selector(openURL:) withObject:[NSURL URLWithString:@"extensionShare://"]];
}
}
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
}
}];
}
}
Demo
demo
遗留问题 问题一:点击分享不响应问题App中添加了Share Extension
,又配置了Document Type
,d出来的分享面板出现了拷贝到xxx app
,和xxx app
两个应用入口,点击拷贝到xxx app
后成功分享,再点击xxx app
发现不响应了。
目前不知道该如何解决,只能关闭Document Type
的配置,但这样隔空投送过来的文件就无法在app打开。
有遇到这个问题的兄弟们帮忙留个言。
正常的文件传输收到的数据类型基本上是NSURL
的文件路径格式,而类似于分享.db
的文件类型,收到的数据类型是NSData
的路径格式。
这时候怎么办呢,第一想法肯定是采用data
转string
获取文件路径,思路是没错的:
[[NSString alloc] initWithData:data] encoding:NSUTF8StringEncoding];
可是返回的是nil
。这是因为data中存在非UTF8
编码格式的字符。
暂时的解决办法参照编码转化
如有更好的解决办法可以留言下,万分感激!
iOS App Extensions初识及工作原理
iOS App Extensions之Action Extension
https://www.cnblogs.com/junhuawang/p/8182868.html
https://www.jianshu.com/p/01c933254e66
https://stackoverflow.com/questions/24297273/openurl-not-work-in-action-extension/28037297#28037297
https://www.thinbug.com/q/56438916#google_vignette
https://developer.apple.com/forums/thread/65621
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)