美文网首页iOSIOS文章收集iOS实战
通过UIDocumentInteractionControlle

通过UIDocumentInteractionControlle

作者: SeraZheng | 来源:发表于2015-12-31 10:12 被阅读9965次

前言

朋友分享推荐给我一本PDF格式的史蒂夫•乔布斯传,阅读了几篇,很受感触,于是想把他分享给大家欣赏阅读。早起闲来无事,正好就接着写篇文章来分享一下!我在“iOS实现App之间的内容分享”这篇文章中详细讲解了通过注册UTI的方式让我们的App支持分享,也简单地说了一下App内部怎么处理分享。同时,我也指出了在iOS系统跨App分享内容的几种常用技术,比如URL Scheme,AirDrop, UIDocumentInteractionController,UIActivityViewController这几种。这一篇文章,我们来谈一下最基础的原始方法,怎么通过使用UIDocumentInteractionController来预览、操作和分享史蒂夫•乔布斯传

简介

从iOS SDK的API文档中,我们可以找到UIDocumentInteractionController的声明:

NS_CLASS_AVAILABLE_IOS(3_2) __TVOS_PROHIBITED @interface UIDocumentInteractionController : NSObject <UIActionSheetDelegate>


由此声明我们可以得知,`UIDocumentInteractionController`是从iOS 3.2的SDK开始支持的,它是直接继承的`NSObject`,而不是我们想象的`UIViewController`,因此我们需要使用`UIDocumentInteractionController`提供的方法来展示它,而且我们还可以看出它是不能在Apple TV 的开发中使用的。遍观`UIDocumentInteractionController`的属性和方法可以看出,`UIDocumentInteractionController`主要给我们提供了三种用途,我会在下面的内容中逐条的讲解`UIDocumentInteraction`的每一种用途的具体使用:
>1. 展示一个可以操作我们分享的文档类型的第三方App列表
2. 在第一条展示列表的基础上添加额外的操作,比如`复制`,`打印`,`预览`,`保存`等。
3. 结合`Quick Look`框架直接展示文档内容

#准备阶段
首先我创建了一个新的应用方便演示和截图,我把它命名为`ZSDocumentInteractionTest`,然后拖入PDF格式的`史蒂夫•乔布斯传`到`ZSDocumentInteractionTest`项目的bundle中。然后在`Storyboard`的`ViewController`中添加了一个Button作为`UIDocumentInteractionController`的触发操作(这些操作都比较简单,就不在这里用图展示啦)。运行程序,我们就可以看到Button啦,截图如下。然后我们就可以在Button的触发方法中,操作`UIDocumentInteractionController`来显示或者分享我们的`史蒂夫•乔布斯传`啦,具体的应用详情可以参考GitHub上的Demo:[ZSDocumentInteractionTest](https://github.com/SeraZheng/ZSDocumentInteractionTest)。

![在ViewController的视图 中显示Button](http:https://img.haomeiwen.com/i1216322/9fac2efc2d9a6bc4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#初始化
不管我们使用哪种`UIDocumentInteractionController`的展示方式和用途,都需要给`UIDocumentInteractionController`指定文档的URL,所以我们通常使用下面的初始化方式,给`UIDocumentInteractionController`指定`文件的URL`。
  • (IBAction)presentPDFDocumentInteraction:(id)sender {
    UIDocumentInteractionController *documentController = [UIDocumentInteractionController interactionControllerWithURL:[[NSBundle mainBundle] URLForResource:@"Steve" withExtension:@"pdf"]];
    }

#展示第三方App列表
我们先实现`UIDocumentInteractionController`的第一个用途,展示可以操作PDF文件的第三方App列表。我们需要使用`UIDocumentInteractionController`提供的方法:
  • (BOOL)presentOpenInMenuFromRect:(CGRect)rect inView:(UIView *)view animated:(BOOL)animated;

我在Button的触发方法中添加下面的代码,意思就是让`UIDocumentInteractionController`的View在当前控制器视图上显示:
[documentController presentOpenInMenuFromRect:self.view.bounds inView:self.view animated:YES];
运行程序,点击Button,我们可以开始第一次展示测试啦。

#第一次展示测试
一切准备就绪之后,我开始进行`UIDocumentInteractionController`的测试,点击Button,就可以看到下面的界面啦。这说明我们的第一步成功了!!(真棒)

![可以操作PDF文件的第三方App列表](http:https://img.haomeiwen.com/i1216322/f1a0a8924ea4b42d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

简单介绍一下这个界面,这个视图中的第一行列表显示`AirDrop`,是苹果在`iOS 7`提供的一种跨设备分享的技术,我会在后边的文章中讲解。视图中的第二行列表就是整个iOS系统中,可以操作PDF文档的应用程序列表,还包括了苹果在`iOS 8`提供的`Share Extension`图标,关于`Share Extension`,我会在后边的文章中讲解。视图中的第三行列表,就是现实设备可选的操作,如`Copy`,`Print`中,这里什么操作都没有,并不是说没有可执行的操作,而是我们没有让他显示出来。

接着我试着点击QQ图标,打算把`史蒂夫•乔布斯传`分享给我的好友,然而意外发生了,`ZSDocumentInteractionTest`崩溃掉啦,而且还给出我们一段错误提示:

2015-12-30 19:00:40.078 ZSDocumentInteractionTest[1254:344240] *** Assertion failure in -[_UIOpenWithAppActivity performActivity], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3512.29.5/UIDocumentInteractionController.m:408
2015-12-30 19:00:40.079 ZSDocumentInteractionTest[1254:344240] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UIDocumentInteractionController has gone away prematurely!'
*** First throw call stack:
(0x248e185b 0x35fa2dff 0x248e1731 0x25672ddb 0x290638c9 0x292695bb 0x28d5aefd 0x28d5e1a1 0x28b42107 0x28a50a55 0x28a50531 0x28a5042b 0x282e05cf 0x1acd03 0x1b17c9 0x248a4535 0x248a2a2f 0x247f50d9 0x247f4ecd 0x2db6aaf9 0x28a7e2dd 0x780ad 0x366f0873)
libc++abi.dylib: terminating with uncaught exception of type NSException

我看到错误提示竟然指向了`UIDocumentInteractionController.m`文件,而且错误提示是`NSInternalInconsistencyException`(内部不一致)和"UIDocumentInteractionController has gone away prematurely!"(UIDocumentInteractionController过早地被释放掉啦)。由此我想出这个应该是内存过早释放的一个错误,然后我查阅了一下Apple Developer上的文档,原来,在ARC环境下展示`UIDocumentInteractionController`时,当我的函数方法调用完毕,退栈之后,`UIDocumentInteractionController`的实例就被释放掉了,展示出来的这个View由`Quick Look`框架来操作,并不会对`UIDocumentInteractionController`产生引用。当点击View上面的Button时,内部操作仍然会继续访问这个`UIDocumentInteractionController`实例,就会报出上述错误。

错误原因找到了,那么解决原理也就清楚了,只要不让`UIDocumentInteractionController`实例过早释放就可以啦。我们可以将`UIDocumentInteractionController`声明为一个`strong`类型的实例属性,然后修改一下Button触发方法就可以啦。(仍然不理解的朋友可以去GitHub上下载Demo测试)

@interface ViewController ()
@property (nonatomic, strong) UIDocumentInteractionController *documentController;
@end

我在Button的触发方法中添加下面方法的调用,为了方便区分和理解,我把代码封装成了私有实例方法:
  • (void)presentOpenInMenu
    {
    // display third-party apps
    [self.documentController presentOpenInMenuFromRect:self.view.bounds inView:self.view animated:YES];
    }

```Objective-c
- (IBAction)presentPGNDocumentInteraction:(id)sender {
    _documentController = [UIDocumentInteractionController interactionControllerWithURL:[[NSBundle mainBundle] URLForResource:@"Steve" withExtension:@"pdf"]];
    [self presentOpenInMenu];
}

修改完之后,运行程序,然后点击Button,看到第一次测试时展示出来的图片啦。然后再点QQ图标,就可以正确地跳转到QQ程序中,选择好友就可以分享史蒂夫•乔布斯传啦。(QQ接收分享页面就不展示了,想试验的可以手动测试下)

展示可选操作

我们可以看到第一步图示里面只有App图标,第二行操作列表中只有一个More。所以我们来展示UIDocumentInteractionController的第二种用途,在第一步的基础之上,显示附加的操作选项,。这需要我们使用UIDocumentInteractionController提供的另外一种展示方法:

- (BOOL)presentOptionsMenuFromRect:(CGRect)rect inView:(UIView *)view animated:(BOOL)animated;

我们在Button的触发方法中添加下面方法的调用:

- (void)presentOptionsMenu
{
    // display third-party apps as well as actions, such as Copy, Print, Save Image, Quick Look
    [_documentController presentOptionsMenuFromRect:self.view.bounds inView:self.view animated:YES];
}

运行程序,点击Button,我们可以看到下面的界面,多了CopyPrint的操作。Copy操作可以将文件拷贝到系统粘贴板中,而Print操作则是关联打印机进行打印操作的。(在这里我就不展示这俩种操作的具体界面啦!)

第三方App列表和附加操作界面
如果UIDocumentInteractionController关联的是一个图片文件,这个界面还会提供一个Save Image的操作,用来直接保存图片到系统的Photos中,此外这个界面还提供了一个Quick Look操作,可以让我们直接预览乔布斯自传PDF文档,只不过需要我们再多写点代码,为了文章的合理性和结构性,我决定在下面的标题内容中讲解。(先卖个小关子!!)

直接预览

UIDocumentInteractionController第三种预览文档内容的用途非常重要,而且也是常见的。我会详细地说一下如何通过UIDocumentInteractionController实现预览史蒂夫•乔布斯传。首先你需要为UIDocumentInteractionController指定一个delegate,并且实现下面的代理方法:

- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller;

这个代理方法主要是用来指定UIDocumentInteractionController要显示的视图所在的父视图容器。这样UIDocumentInteractionController才清楚在哪里展示Quick Look预览内容, 我在这里就指定Button所在的UIViewController来做UIDocumentInteractionController的代理对象,并且实现上面的代理方法。在Button的触发方法中添加下面的代码

_documentController.delegate = self;

然后实现代理方法:

- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller
{
    return self;
}

UIDocumentInteractionController是继承自NSObject的,因而为了能够实现直接预览,我们需要用到UIDocumentInteractionController提供的展示预览的方法,

- (BOOL)presentPreviewAnimated:(BOOL)animated;

这个方法是以模态窗口通过Quick Look框架全屏显示PDF的内容,所以我们在Button的触发方法中添加下面方法的调用:

- (void)presentPreview
{
    // display PDF contents by Quick Look framework
    [self.documentController presentPreviewAnimated:YES];
}

然后运行程序,点击Button,弹出了一个新视图,可以看到史蒂夫•乔布斯传的内容,如下图

直接预览乔布斯自传

展示预览操作

通过上面的操作我们就可以欣赏阅读我们想看的史蒂夫•乔布斯传啦,不过别忘记我们上面还卖了一个小关子,就是在展示可选操的时候,除了CopyPrint,其实我们还可以展示Quick Look这个预览操作。为什么我要卖关子呢,因为我是一个相信因果循环的人,我组织文章的逻辑是由浅入深,我设想通过一步步铺垫来展开UIDocumentInteractionController所有特性。

好啦,回归正题!我们想要实现显示Quick Look预览操作,其大部分的工作在直接预览这一小节中都做完了,比如指定代理对象,然后实现这个代理方法来指定UIDocumentInteractionController的父视图容器:

- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller;

由于我们已经做完了所有准备,在这一步,我们只需要将直接展示史蒂夫•乔布斯传内容的方法替换为下面这段,展示可选操作列表的方法,就可以啦!

- (void)presentOptionsMenu
{
    // display third-party apps as well as actions, such as Copy, Print, Save Image, Quick Look
    [_documentController presentOptionsMenuFromRect:self.view.bounds inView:self.view animated:YES];
}

然后我们运行程序,点击Button,就可以看到Quick Look操作已经显示出来啦!如下图:

展示Quick Look操作

如果我们点击这个Quick Look操作,就可以看到直接预览内容时所展示的界面啦。好啦,通过UIDocumentInteractionController实现史蒂夫•乔布斯传的预览和分享就到此结束啦。我会在下面的章节中,讲解通过其他技术实现乔布斯自传的分享和操作。

相关文章

  • 通过UIDocumentInteractionControlle

    前言 朋友分享推荐给我一本PDF格式的史蒂夫•乔布斯传,阅读了几篇,很受感触,于是想把他分享给大家欣赏阅读。早起闲...

  • 利用UIDocumentInteractionControlle

    /** txtg格式乱码问题解决方法 */ 参考链接:http://blog.163.com/lxl_dml/bl...

  • 使用UIDocumentInteractionControlle

    背景一: 在app中需要打开类似于 word pdf ppt 等等文件 做一个预览背景二: 所有文件都是进行的本...

  • 使用UIDocumentInteractionControlle

    引言:iOS对浏览文件提供了方便的SDK,让开发者调用实现各自的需求,之前利用UIWebView直接加载本地或者网...

  • 使用UIDocumentInteractionControlle

    这次做的项目中需要分享图片到Instagram,于是看了下Instagram的官方文档,要分享图片到Instagr...

  • 关于UIDocumentInteractionControlle

    关于UIDocumentInteractionController     UIDocumentInteracti...

  • 通过啦通过啦

    今天算是成了一件大事吧,暂定下周一,所以要记得明天请假。 今天到账了一点,把想好的捐款捐了,小姑娘怪不容易的。 晚...

  • 通过

    离离张贺原上草

  • 通过

    2018年10月30日 耗时4个月的考试终于出结果了,通过了! 看到结果的那一刻,内心的狂喜,激动,真是难以言表,...

  • 通过

    sel是通过取得sel来直接调方法, self来执行方法是通过消息传递机制来调用方法

网友评论

  • 手中的风信子:想再iPad上使用,但是presentOpenInMenuFromRect 没反应,楼主知道如何解决嘛?
  • 韩马小执:怎么去掉分享按钮那?
  • 4645535895e9:在真机上运行,预览时报错
    Couldn't issue file extension for url: file:///var/containers/Bundle/Application/1007D460-10BC-42D0-AD82-62CAC0710EE6/xxx.app/xxx.pdf
    是什么原因
    不舍红尘的和尚松:@type_eleven ios11 点击拷贝到其他应用打不开 有没有遇到这个问题
    Chase_Eleven:@ll风随心动 这个问题你解决了吗?我现在也遇到这个问题,iOS11有问题,iOS10没问题
    4645535895e9:使用UIWebView加载同样的url能够正常显示
  • 新地球说着一口陌生腔调:我打开txt文件 显示乱码如何解决呢?
  • 吃货_X:请问,在文档预览时。点击预览界面里面的可点击项报错。-[NSURL isTelephonyURL]: unrecognized selector sent to instance 0x1702df1e0
    SeraZheng:传参的类型不对
  • 21175fa2b13d:很好,学习了。另外乔布斯传也收下了。
  • JJ_Liang:楼主你好,App里打开图片文件,可选方式列表里没有美图秀秀,360相机等应用。是因为这些应用没有注册图片类型的UTI吗?
    JJ_Liang:@SeraZheng 感谢回复。猜测应该是没有注册image的UTI。格式JPG、png都不行。数量都是打开单张图片。测试:自己写Demo注册了image的UTI,打开可选列表是有自己写的Demo的。
    SeraZheng:原因有很多,比如图片格式,数量等等
  • GavinKang:COOL,我们现在的需求是,只分享到一个平台,可以吗??例如分享ZIP格式的到QQ,但是现在点击分享的时候,弹出来全部的,感觉不是太好
    SeraZheng:你的需求就不能用这个技术了,用QQ 开放平台SDK或统一集成SDK
  • 90894b245755:自己的应用中,用QQ打开,发送给朋友,每次都显示发送失败请重新尝试分享~。LZ求教
    SeraZheng:看看打开Controller时,文件加载路径是否正确呢
  • ec4985ea6ba3:请教下 [_documentController presentOptionsMenuFromRect:self.view.bounds inView:self.view animated:YES]; 调用这个方法后 还是不出来Quick Look
  • 4090dea7ba26:2017-01-03 15:04:07.459 Test[1184:673582] Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UIAlertController: 0x155f6ece0>)
    2017-01-03 15:04:08.121 Test[1184:673582] Unbalanced calls to begin/end appearance transitions for <QLRemotePreviewContentController: 0x156061600>.
    2017-01-03 15:04:09.154 Test[1184:673582] Cannot find preview item for proxy: <QLPreviewItemProxy: 0x155f75990> - 程序猿的自我修养.pdf (0)

    使用Quick Look 打开的,查看正常, 就是会报这样的问题?本人菜鸟,求解!
  • 小蜗牛吱呀之悠悠:还有一个问题哦,请问一下,取消按钮这些文字怎么改成中文的呢
  • 小蜗牛吱呀之悠悠:文章很好,感谢楼主,小朋友有个疑惑,请假一下楼主。在可选列表中显示直接预览的部分显示不出来哦,按照楼主的Demo来也不行
  • 搬砖的小红帽:感谢LZ,找了好久终于找到。如果手机内没有应用可以操作该格式的文档呢?有没有办法可以获取到是否有应用可以操作文档?
    SeraZheng:@老林冬瓜 可以的
    Kevin_Ray:请问楼主用这个可不可以预览在线的文档,不是本地的
    SeraZheng:@搬砖的小红帽 这个应用获取是iOS 系统基于UTI的一套推荐搜索,暂时不能和Mac系统一样,从APP Store联想到推荐应用
  • Jaesun:太好了,有这篇文章,轻松完成需求
  • 203b4353d989:在这里找到了答案 666 深奥
  • WintersMoon:写的很不错,不过我有两个迫切的问题需要请教,希望解答一下,不胜感激.
    问题1:使用presentOpenInMenuFromRect:方法那个view弹不出来,你给的demo中也是,这是什么情况?
    问题2:里面的应用列表可以自定义吗,还是只要手机里面的应用支持这个类型的文件就会出现在里面,可以自己决定哪些应用出现吗?
    SeraZheng:@WintersMoon 自定义应用列表不是开发者决定的,而是iOS系统的使用者决定的,“More”这个操作就是给用户提供自主排列应用优先级的
    WintersMoon:@SeraZheng 关于自定义应用列表可以再单独展开说一说吗,或者提供个demo示例,因为这方面一直没找到相关资料。
    SeraZheng:@WintersMoon 更多按钮就是做第二个需求的,可以选择应用列表的关闭和顺序优先级,第一个代码逻辑可能是,这里是正常的
  • hY_Ramos:你好!问下预览时可不可以不presentPreviewAnimated,在当前的viewController进行预览,自定义预览视图大小?
    hY_Ramos:@SeraZheng 好的,谢谢!
    SeraZheng:@hY_Ramos 预览可以用QuickLook,也可以用WebView
  • 会疼的白痴:你好我想问一下,怎么分享多个文件,你说的UTI在哪里啊
    SeraZheng:@会疼的白痴 设置info.plist的Document Type属性!具体值可以查官方文档
  • EdLiya:一颗赛艇 !
  • df034c3f9f9d:想问下,这个能同时分享多个文件?
    SeraZheng: @df034c3f9f9d kUTTypeURL应该可以
    df034c3f9f9d:@SeraZheng 分享链接时,LSItemContentTypes应该写什么
    SeraZheng: @df034c3f9f9d 可以,项目的UTI设置中可以规定最大值,设定同时发送的文件数
  • 11bfc71c3fe3:我用UIActivityViewController 为什么第二行不显示copy to 第三方的app呢
  • SeraZheng:@无谓的执恋 这种系统级的显示,根据的是系统的偏好语言设置,把系统语言设置成汉语,显示出来的就是汉语啦!比如苹果系统自带的Photos应用
    筑梦师Winston:在info.plist文件Localization native development region 配置为 China
    Echo126:@1658786291 你解决了吗?我也遇到了,本身手机是中文,但是显示出来的全是英文的,比如复制显示的copy。
    1550e427c1ee:@SeraZheng 本身手机就是中文啦,为什么还是不能把copy转换为复制
  • 6b9801f1b8f7:这样写出来的显示的英文,如何设置成中文
    a28bec5b63af:info.plist中添加Localizations(数组类型),展开后将第一个item设置为chinese
    古子林:更改APP支持的语言类型。
    1550e427c1ee:@无谓的执恋 你设置成功了吗
  • 伤心小剑:请问lz有没有方法能调整第三方App列表内app的顺序
    SeraZheng: @伤心小剑 是的
    伤心小剑:@SeraZheng 那也就是说如果第三方app能把自己的uti调高就可以默认在最前边了?
    SeraZheng: @伤心小剑 可以!每一行后面都有个More,可以调整顺序。除此之外,代码控制顺序是不可能的,这个顺序应该是根据每个App中UTI的层级结构出来的,没法控制第三方App的UTI
  • ef1b354c2476:为什么展示可选操作没办法显示?只要多加presntOpenInMenuFromRect这个方法就行吧?可是还是只有一个more
    ef1b354c2476:@SeraZheng 我刚才是打错了。。
    ef1b354c2476:@SeraZheng 对啊,可是没办法显示额,
    SeraZheng: @SourceC 展示可选操作,调用这个方法,
    - (void)presentOptionsMenu {
    // display third-party apps as well as actions, such as Copy, Print, Save Image, Quick Look [_documentController presentOptionsMenuFromRect:self.view.bounds inView:self.view animated:YES]; }
  • JanzTam:文章很棒!期待更多技术好文分享!
    SeraZheng:@JanzTam 谢谢!
  • 曾樑:Cool
    SeraZheng:@曾樑 谢谢

本文标题:通过UIDocumentInteractionControlle

本文链接:https://www.haomeiwen.com/subject/ilpihttx.html