使用UIDocumentInteractionControlle

作者: ibabyblue | 来源:发表于2016-09-07 16:24 被阅读7204次

    引言:iOS对浏览文件提供了方便的SDK,让开发者调用实现各自的需求,之前利用UIWebView直接加载本地或者网络路径浏览文件。最近研究了一下UIDocumentInteractionController和QLPreviewController去预览文件,个人感觉各自有各自的优缺点,需要根据具体的业务场景选择适合的方式。


    UIDocumentInteractionController的使用方法,以及QuickLook的使用方法,网上资料已经很多了,这里记录一下自己学习所获得的知识,和一个自定义的问题。
    一、是否可以在线预览
    刚开始接触时的想法是:是否有在线预览功能,UIDocumentInteractionController和QLPreviewController的显示样式自带分页效果,效果看起来和专业做文档预览的软件,预览效果类似,那不是太好了!然而现实很骨感,查阅了大量资料以及自己测试之后,我可以确定的答案是:不能在线预览!只能加载本地文件。
    二、UIDocumentInteractionController
    使用过UIDocumentInteractionController的小伙伴都知道,虽然它叫控制器,但是它不是真正意义上的控制器而是继承自NSObject的。这也就是为什么要遵循代理方法,告诉UIDocumentInteractionController如何去显示,比如显示在当前控制器,显示的Rect等。
    下面是实现代码:

    #import "ViewController.h"
    
    @interface ViewController ()<UIDocumentInteractionControllerDelegate>
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.navigationItem.title=@"预览";
        
        NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"iOS开发指南.pdf" ofType:nil];
        NSURL *url = [NSURL fileURLWithPath:urlStr];
        
        UIDocumentInteractionController *documentVc = [UIDocumentInteractionController interactionControllerWithURL:url];
        documentVc.delegate = self;
        
        [documentVc presentPreviewAnimated:YES];
        
    }
    
    #pragma mark - UIDocumentInteractionController 代理方法
    - (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller{
        return self;
    }
    
    - (UIView *)documentInteractionControllerViewForPreview:(UIDocumentInteractionController *)controller{
        return self.view;
    }
    
    - (CGRect)documentInteractionControllerRectForPreview:(UIDocumentInteractionController *)controller{
        return self.view.bounds;
    }
    

    显示的样子是这样的:

    UIDocumentInteractionController-1.png

    如果下面的代理方法不实现的话

    - (UIView *)documentInteractionControllerViewForPreview:(UIDocumentInteractionController *)controller{
        return self.view;
    }
    

    是这样子的:

    UIDocumentInteractionController-2.png
    在图2的状态单击也会出现图1的效果,隐藏导航栏。但是有个问题是,无法自定义这个导航栏,可能说无法自定义这有点绝对,只是我现在还没有找到好的办法,如果有哪位知道的话,烦请指教,谢谢!
    三、QLPreviewController
    QLPreviewController的实现和UIDocumentInteractionController类似,但是QLPreviewController是真正的控制器。两者之间虽然实现效果相同,但是还是存在区别的。
    比如:QLPreviewController可以一起浏览多个文件,而UIDocumentInteractionController一次只能浏览一个文件。
    使用QLPreviewController之前,需要导入QuickLook.framework,并遵守其数据源和代理方法。实现代码如下:
    #import "ViewController.h"
    #import <QuickLook/QuickLook.h>
    
    @interface ViewController ()<QLPreviewControllerDataSource,QLPreviewControllerDelegate>
    
    @end
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //控制当前控制器对应的导航条显示的内容
        self.navigationItem.title=@"预览";
        
        NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"iOS开发指南.pdf" ofType:nil];
        NSURL *url = [NSURL fileURLWithPath:urlStr];
        self.fileUrl = url;
        
        if ([QLPreviewController canPreviewItem:(id<QLPreviewItem>)url]) {
            QLPreviewController *qlVc = [[QLPreviewController alloc] init];
            qlVc.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
            qlVc.delegate = self;
            qlVc.dataSource = self;
            qlVc.navigationController.navigationBar.userInteractionEnabled = YES;
            qlVc.view.userInteractionEnabled = YES;
            [self presentViewController:qlVc animated:YES completion:nil];
        }
    }
    
    #pragma mark - QLPreviewController 代理方法
    - (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller{
        return 1;
    }
    
    - (id<QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index{
        return self.fileUrl;
    }
    
    @end
    

    实现的效果如图2是一样的,但是使用QLPreviewController能够实现替换导航栏的问题,比如我们的需求为不需要右上角调出Menu项供选择,但是需要标题和返回按钮,在下面的代码中这样修改:

    if ([QLPreviewController canPreviewItem:(id<QLPreviewItem>)url]) {
            QLPreviewController *qlVc = [[QLPreviewController alloc] init];
            qlVc.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
            qlVc.delegate = self;
            qlVc.dataSource = self;
            qlVc.navigationController.navigationBar.userInteractionEnabled = YES;
            qlVc.view.userInteractionEnabled = YES;
            [self.view addSubview:qlVc.view];
            [self presentViewController:qlVc animated:YES completion:nil];
        }
    

    将QLPreviewController的view添加到当前控制器的view上,如下:

    [self.view addSubview:qlVc.view];
    
    QLPreviewController-3.png

    但是这样的话又出现了新的问题,其本身的导航栏不见了,显示在你眼前的是你自己导航栏,如果需求改为需要右上角功能,这时就尴尬了,所以这其中的取舍需要具体的业务来决定了。
    接下来看下QLPreviewControllerDataSource方法:

    - (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller{
        return 1;
    }
    

    这里我返回的PreviewItems的数量为1,就是一次加载一个文件,这里可以是多个文件的数组个数,也就是某个本地路径下的多个文件,大家可以自己实现下。

    - (id<QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index{
        return self.fileUrl;
    }
    

    返回的当前预览的文件QLPreviewItem,controller:当前预览控制器,index:当前预览的第几个文件。 这里为什么返回Url呢,进入QLPreviewItem头文件你就会明白了:

    QL_EXPORT @protocol QLPreviewItem <NSObject>
    
    @required
    
    /*!
     * @abstract The URL of the item to preview.
     * @discussion The URL must be a file URL. 
     */
    @property(readonly, nonnull, nonatomic) NSURL * previewItemURL;
    
    @optional
    
    /*!
     * @abstract The item's title this will be used as apparent item title.
     * @discussion The title replaces the default item display name. This property is optional.
     */
    @property(readonly, nullable, nonatomic) NSString * previewItemTitle;
    
    
    @end
    
    /*!
     * @abstract This category makes NSURL instances as suitable items for the Preview Controller.
     */
    @interface NSURL (QLPreviewConvenienceAdditions) <QLPreviewItem>
    

    其实NSURL的分类遵循了QLPreviewItem协议,所以是可以返回Url的,这里可以返回一个Url数组。这就是预览多个文件时的数据源方法,注意:以上两个方法是@required的,必须需要实现的。
    QLPreviewController还有一些代理方法,供你选择性实现,比如说:

    - (void)previewControllerWillDismiss:(QLPreviewController *)controller;
    

    QLPreviewController将要消失的时候你可以处理一些逻辑,还有一些就不列举了,大家可以进入头文件了解一下。
    自定义预览控制器,一直没有好的办法,如有知道的童鞋,烦请指教下,顺便说下,刚刚接触这块,如有错误,也请指出,多谢!

    相关文章

      网友评论

      • 风衫码农:楼主 可以监听 PDF当前预览的是第几页 还有 可以加载时候手动设置加载到相应的页码吗?
      • 75b8ffd94017:你好,,
        UIDocumentInteractionController 可以让他显示到最底部吗? 最下面的内容
      • 走停2015_iOS开发:QLPreviewController 现在为什么只显示标题 不显示内容
        SoulmateA:@ibabyblue 是的,我也遇到这个问题了,内容显示不出来。你解决这个问题了吗?
        SoulmateA:是的,我也遇到这个问题了,内容显示不出来。你解决这个问题了吗?
        ibabyblue:@走停2015_iOS开发 这个不能吧,可以显示啊
      • 韩马小执:怎么把分享按钮给去掉啊
        ibabyblue:@韩马小执 自定义控制器吧
      • l宁采臣l:老铁,用UIDocumentInteractionController打开文件的时候会跳到一个新的控制器,不是原先的控制器,我现在原控制器打开该怎么办,谢谢~
        ibabyblue:@l宁采臣l 嗯,webview可以实现一些功能,但有些时候,使用webview会很蛋疼,并不合适。不过实现功能就好。
        l宁采臣l:@ibabyblue 我这边使用webview预览的,已经实现功能了,感谢老铁
        ibabyblue:使用QLPreviewController,添加到当前控制器的view上。
      • 新地球说着一口陌生腔调:你好 我用UIDocumentInteractionController加载txt文件,里面的内容是乱码怎么解决了?
      • 梦魇丶:博主你好,请问可以实现ppt分页预览的效果吗,不是从上到下铺满的,而是可以左右滑动翻页的,安卓的好像有这个效果,iOS我没找到
      • iOS_Ken:你好~刚刚测试过报以下问题2017-08-05 09:48:08.947 Ucacc[3847:61567] BOM void _BOMExtractionFatalFileHandler(BOMCopier, const char *, int) Could not extract archive for file /file:/Users/ken/Library/Developer/CoreSimulator/Devices/07E911F0-3EAF-41D6-9193-106DDEA8A61F/data/Containers/Data/Application/BF345E52-29CB-436F-B5B6-E63D21BD8AF3/Documents/assets.zip: No such file or directory,但是在沙盒里面是有的!请问是什么原因?
        iOS_Ken:对的~~rar的报错
        ibabyblue:@c7a707cf93bf 你这文件是压缩包?
        iOS_Ken:要第二次进画面才会显示文件内容!
      • 吃货_X:请问,在点击预览里面的连接时报错-[NSURL isTelephonyURL]: unrecognized selector sent to instance 0x1742c0d20怎么办?例如点击效果图里面的“框架”。
        吃货_X:@Cloud_90 谢谢。我把那个功能去掉了,只能让他分享出去或者用其他应用大家。
        ibabyblue:嗯,你这个需求我之前没有遇到,所以也没研究过,也不能断然下结论,抱歉不能帮到你。
      • 7c2745ebadda:是图片的话,弹出来的只有icloud分享,不能用系统相册打开吗?
      • 新地球说着一口陌生腔调:UIDocumentInteractionController和QLPreviewController 都不能加载网络url吗?
        ibabyblue:@新地球说着一口陌生腔调 自定义控制器,但是这样的话自带的工具栏就不见了,文章中写的很清楚啊,只是去除单一按钮,我没有找到好的方法。
        新地球说着一口陌生腔调:@Cloud_90 怎么去掉自带的分享工具栏按钮
        ibabyblue:@新地球说着一口陌生腔调 恩 是这样的
      • GY玩玩:博主 你好 请问下想在线预览文件(比如pdf,word,excle...)有没有好的方法或框架推荐,服务器只给返回个附件的路径:pray: :pray: :pray:
        ibabyblue:@GY玩玩 客气
        GY玩玩:@Cloud_90 恩 谢谢你的帮助:+1:
        ibabyblue:@GY玩玩 你好,你这需求我之前也是这样的,但是我没有找到合适的办法,这篇文章也是在无法在线浏览的情况下,才采取的这种方式去实现,都为下载到本地浏览!之前在做此需求的时候,我研究过亿方云等软件,发现他们也是下载到本地浏览的,但是他们做了一个在线浏览的假象!就是有个生成浏览的过程,其实就是下载了在浏览!因为后续没有这方面需求,所以没有跟进研究!以上为个人见解可能有误!供你参考!
      • 哈么么茶:怎么禁止复制 粘贴呢
        ibabyblue:@哈么么茶 抱歉好久没来了 ,这个需求我没有遇到,所以没有尝试过,就不能乱说了...见谅!
      • JohnQ:博主 问你一个问题我也用这个控件进行预览文档 我有两个地方进行入口预览 一个进入预览页面没有done和那个分析按钮 还有一个入口则有 而且我的这个分析按钮在左下角 不知道博主你遇到过没
        ibabyblue:@JohnQ 我试了下,现在的分享按钮默认是在左下角的,和我做的时候不一样了!我也没有找到好的办法,你这样吧 不存在按钮的那个入口,自定义控制器!存在按钮的入口,选用原生!如果有好的办法,请反馈下哦。
        JohnQ:@Cloud_90 我可以说我现在还没找到解决方法吗:cry::cry::cry:
        ibabyblue:@JohnQ 抱歉好久没来了,没有done和分析按钮是可以的!在左下角这个我记得好像是可以自定义位置的,时间有点久,记不清了。这么久了也许你解决了,有什么好的方法,可以回复我一下,共同学习!
      • 愚人船ios:刚打开的时候会有一段时间去加载啊 这么判断加载完了啊?
      • coderChrisLee: 写得很棒👍,最近做文档预览里面遇到一些坑。可以返回一个Url数组 ?这个我得试下。
        coderChrisLee:在numberOfPreviewItems代理方法里面可以返回多个文档的个数,但在previewItemAtIndex代理里面每次只能返回一个NSURL类型的QLPreviewItem(即file URL),我们是根据URL做网络请求,只有下载到沙盒之后才可以展示预览。多个文档预览时,比如8个,需要跟随用户向右滑动时候才去下载滑动所到的文档,当没有下载完成的时候预览是灰色的,也可以快速向右滑动,但此时就会出现一个问题:当快速从第1个滑倒第8个时,异步请求返回数据并没有顺序,第一个会进入预览的时候就立即请求并完成展示,但是后面的第2~8个却可能会出现由于滑动太快还没来得及请求完成而无法显示预览,此时回退滑动,就无法获取到previewController的currentItemIndex(只有在有本地文件可以预览时才能获取到currentItemIndex,未下载完成时为NSNotfound这个最大值,容易造成根据这个index从数据源数组中取值时崩溃),这种问题如何解决呢? 是不是需要滑动时每次建立请求,把请求放入队列中操作?
        ibabyblue:@coderChrisLee 谢谢!
      • 捞月亮的猴子:使用ionic的一个插件的时候,安卓实现了在线和本地的图片预览,iOS原作者使用了UIDocumentInteractionController,导致不能在线图片预览,唉,一步一个坑。
        ibabyblue:@捞月亮的猴子 嗯,只是预览图片的话,用第三方框架吧
        捞月亮的猴子:@Cloud_90 是啊,原生的缓存到本地还好实现。h5有些框架缓存不太好弄。
        ibabyblue:不能在线预览,确实伤。

      本文标题:使用UIDocumentInteractionControlle

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