iOS App接收其它应用数据机制实现
在不同应用中共享数据,这是很有实际意义的,比如果我现在做的iPad打印程序,想从PC机传输一个打印脚本通过App ,控制打印机打印,自己写一套传输机制工程量相当大.
早期类似功能,我使用App打开文件共享功能. 即PC机上iTunes软件通过USB联入iPad,然后拖入相应App的文件窗来实现.这个操作对于用户相当麻烦.对于App也好不到哪去,因为App产知道你什么时候拖入,拖入是哪一个文件.
因此我们现在采用iOS 内置App 分享机制. UIDocumentInteractionController 来实现类似需求. 比如用QQ传输文件后,在iPad QQ上文件上直接选其它应用打开. 这样操作简单方便.用户体验很好.
以下就是完整实现这一机制的整个流程
一. info.plist 定义支持文档
这个文章写得很详细.
http://www.jianshu.com/p/88a08d66894f
苹果支持内置分享各种文件类型.
https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html
二.加入自定义后缀文件支持
对于通用文件,如txt,图片文件,大部分应用会自己直接打开,如QQ/微信均是如此,并没有用其它应用打开的选项.因此必须采用一个特定的后缀,QQ才能显示这个选项.
http://blog.csdn.net/chengyingzhilian/article/details/8517193
三.App接收文件处理
会集中在AppDelegate 中处理,
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(nonnull NSDictionary<NSString *,id> *)options{
NSData * recvData = [NSData dataWithContentsOfURL:url];
}
其中文件名称在 url中,options则记录一些选项,比如来源的App
这是我实测一个QQ分享给App结果 URL中,test-1.tscbas就是我分享文件名.如果支持多种后缀,可以通过这个文件名来区分.
file:///private/var/mobile/Containers/Data/Application/B64401CE-7279-4337-837A-1A78BD0B60BA/Documents/Inbox/test-1.tscbas
options的值,可以看到QQ的包名.
{
UIApplicationOpenURLOptionsAnnotationKey = {
};
UIApplicationOpenURLOptionsOpenInPlaceKey = 0;
UIApplicationOpenURLOptionsSourceApplicationKey = "com.tencent.mipadqq";
}
文件本身内容可以接调用 [NSData dataWithContentsOfURL:url] 即可
注意在iOS9.0后,过去一些老的处理接口 handleOpenURL,以及另一个openURL 已经废掉,只采用上述接口
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url NS_DEPRECATED_IOS(2_0, 9_0, "Please use application:openURL:options:") __TVOS_PROHIBITED;
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(nullable NSString *)sourceApplication annotation:(id)annotation NS_DEPRECATED_IOS(4_2, 9_0, "Please use application:openURL:options:") __TVOS_PROHIBITED;
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options NS_AVAILABLE_IOS(9_0); // no equiv. notification. return NO if the application can't open for some reason
四.App界面处理
在QQ分享文件,App可能已经打开,并处在任意一个界面上.这个时间我处理分享文件的界面要如何创建呢? 这个好象没看太多例子,因此通过一些实验,找到办法.
象微信,百度网盘均是在当前界面弹出一个新界面来处理.处理完后,关闭或后退,又会回到原来界面上.因此这可能是大多数App的做法.
因此第一步需要在AppDelegate中找到当前的ViewController,中,可以用这个扩展类
http://stackoverflow.com/questions/11637709/get-the-current-displaying-uiviewcontroller-on-the-screen-in-appdelegate-m
UIWindow+PazLabs.h (header file)
#import <UIKit/UIKit.h>
@interface UIWindow (PazLabs)
- (UIViewController *) visibleViewController;
@end
UIWindow+PazLabs.m (implementation file)
#import "UIWindow+PazLabs.h"
@implementation UIWindow (PazLabs)
- (UIViewController *)visibleViewController {
UIViewController *rootViewController = self.rootViewController;
return [UIWindow getVisibleViewControllerFrom:rootViewController];
}
+ (UIViewController *) getVisibleViewControllerFrom:(UIViewController *) vc {
if ([vc isKindOfClass:[UINavigationController class]]) {
return [UIWindow getVisibleViewControllerFrom:[((UINavigationController *) vc) visibleViewController]];
} else if ([vc isKindOfClass:[UITabBarController class]]) {
return [UIWindow getVisibleViewControllerFrom:[((UITabBarController *) vc) selectedViewController]];
} else {
if (vc.presentedViewController) {
return [UIWindow getVisibleViewControllerFrom:vc.presentedViewController];
} else {
return vc;
}
}
}
@end
找到当前ViewController后直接在上面push一个新的viewController即可.以下是我测试通过代码
UIViewController * vc = [application.keyWindow visibleViewController]; //找到当前窗口
//创建一个新的ViewController
UIStoryboard *secondStoryBoard = [UIStoryboard storyboardWithName:@"PrinterSetting" bundle:[NSBundle mainBundle]];
PrinterSettingViewController* settingController = [secondStoryBoard instantiateViewControllerWithIdentifier:@"SettingView"];
//显示新建界面
[vc.navigationController pushViewController:settingController animated:YES];