使用UIDocumentInteractionController与QLPreviewController进行文件预览

背景一: 在app中需要打开类似于 word pdf ppt 等等文件 做一个预览
背景二: 所有文件都是进行的本地文件预览 网上在线预览无法实现 这个了解到需要后台人员进行配合 提供在线预览的功能
背景三: 在下列方法中使用的沙盒文件路径 是由于已经做过文件存入沙盒处理 所以可以直接取
背景四: 下列所有代码 可以直接新建一个工程 然后拷贝到viewController.m 进行运行 前提是记得要做文件写入沙盒处理 不然会获取不到的

方法一 :使用webView进行 这种方式很简单 只测试了word 可以行的通
   - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //本地沙盒文件路径
    NSString *pathWord = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"word.doc"];
    
    //创建webView
    UIWebView *webV = [[UIWebView alloc]initWithFrame:self.view.bounds];
    [self.view addSubview:webV];
    
    //设置request
    NSURL *fileUrl = [NSURL fileURLWithPath:pathWord];
    NSURLRequest *request = [NSURLRequest requestWithURL:fileUrl];
    
    //加载request
    [webV loadRequest:request];
}
方法二 使用UIDocumentInteractionController 进行预览

看名字有没有一种感觉 一个控制器。感觉还不错 但是你错了 这个不是一个控制器 这个类继承自NSObject
不多说直接看用法 运行起来就行了

@interface ViewController ()<UIDocumentInteractionControllerDelegate>

@property (nonatomic, strong) UIDocumentInteractionController *documentIntController;
@end

@implementation ViewController
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //本地沙盒文件路径
    NSString *pathWord = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"word.doc"];
    
    //创建对象 嵌入文件路径
    self.documentIntController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:pathWord]];
    self.documentIntController.name = @"word.doc";//文件名 不设置也可以 系统会获取到传入文件的名字进行展示  如果自定义的话  可以通过这个进行设置
    self.documentIntController.delegate = self;//代理
    
    
    //下面是三种效果  可以一一打开注释进行查看  同一时间只能运行一种效果
    //    [self.documentIntController presentOptionsMenuFromBarButtonItem:barButtonItem animated:YES];//显示提示框 再选择是否开启预览
    
    
    //    [self.documentIntController presentOpenInMenuFromBarButtonItem:barButtonItem animated:YES];//显示提示框 但是第三行不能选择预览
    
    
    [self.documentIntController presentPreviewAnimated:YES];//直接预览  然后在预览中选择是否使用其他软件打开
}

//UIDocumentInteractionControllerDelegate
//返回的控制器  指明预览将在那个控制器上进行 不进行设置 无法进行预览
- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller {
    return self;
}
@end
  • 注意点:
    一:记得在创建的用来预览的控制器中进行强引用 @property (nonatomic, strong) UIDocumentInteractionController *documentIntController;

    • 理由一:反推,不进行强应用,直接在代码块中进行创建或者进行弱引用 当代码块执行完 UIDocumentInteractionController实例同时被释放 实例代理对象也同时被释放掉了 这个时候点击文件进行预览 谁来响应代理 返回预览控制器 找不到预览控制器 系统怎么知道该在哪里进行预览展示 (这是直接预览的情况)
    • 理由二:可以不直接进行展示 先展示菜单栏(记得在代码块中创建UIDocumentInteractionController对象 弱引用创建属性没用) 在执行代码块的过程中 菜单已经被执行出来了 但是执行完后 对象又被释放了 代理也被释放了 然后进行点击菜单第三方app 或者点击预览的时候 系统又找不到预览控制器了 或者找不到调用者(调用第三方app) 然后直接疯掉 崩溃了(展示菜单栏)

    二:记得代理方法 实现一个方法就可以得到效果 返回的是一个控制器 指明预览将在那个控制器上面进行

三:如果先展示菜单没有任何效果 可能原因是模拟器上没有软件能用来打开文件 (一般这种没有任何反应的 在模拟器上会出现 真机上出现几率不大 原因就是没能找到能开启文件的app 所以就不做展示了)

四:这里只做简单应用 细节问题 提供一个链接http://www.jianshu.com/p/3f03897cf98a

方法三 使用QLPreviewController进行预览

这个就是一个真正的控制器 使用这个的时候 记得导入框架QuickLook.framework 感觉上面有点类似tableView数据源方法 设置数量 然后设置返回的url(类似于tableView里面的cell)

直接代码(新建控制器继承自QLPreviewController 内部实现):

.h文件
@interface YHPreviewController : QLPreviewController
//多文件预览 加载数据
- (void)loadUrlPathList:(NSArray *)urlPathList andCurrentPreVItemIndex:(NSInteger)index;
//单文件预览  加载数据
- (void)loadUrlPath:(NSURL *)fileUrl;
@end

.m 文件
@interface YHPreviewController ()<QLPreviewControllerDelegate,QLPreviewControllerDataSource>

@property (nonatomic, strong) NSMutableArray *fileUrlList;//记录文件路径
@end

@implementation YHPreviewController

- (NSMutableArray *)fileUrlList {//懒加载
    if (!_fileUrlList) {
        _fileUrlList = [NSMutableArray array];
    }
    return _fileUrlList;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    //设置代理
    self.delegate = self;
    self.dataSource = self;
}

#pragma mark -- QLPreviewControllerDelegate  QLPreviewControllerDataSource
//返回数据 数量
- (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller {
    return self.fileUrlList.count;
}
//返回具体的文件路径  直接返回NSURL对象就行   点击QLPreviewitem 进去  滚动到文件最后  你会看到这样一样东西@interface NSURL (QLPreviewConvenienceAdditions) <QLPreviewItem>    NSURL 是遵守QLPreviewItem的
- (id<QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index {
    return self.fileUrlList[index];
}

#pragma mark -- 数据加载   封装的加载方法
//加载数据 同时设置当前预览的文件  
//多文件
- (void)loadUrlPathList:(NSArray *)urlPathList andCurrentPreVItemIndex:(NSInteger)index {
    for (NSURL *urlPath in urlPathList) {
        if ([YHPreviewController canPreviewItem:urlPath]) {//判断是否可以打开文件
            [self.fileUrlList addObject:urlPath];
        }
    }
    self.currentPreviewItemIndex = index;
    [self reloadData];
}
//单文件
- (void)loadUrlPath:(NSURL *)fileUrl {
    if ([YHPreviewController canPreviewItem:fileUrl]) {
        [self.fileUrlList addObject:fileUrl];
    }
    [self reloadData];
}

- (void)didReceiveMemoryWarning {//这个  呵呵......
    [super didReceiveMemoryWarning];
}



//怎么使用创建的控制器  打开文件  调用方法   这里还是在viewController.m里面看调用代码
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //获取沙盒二进制文件路径  多个文件路劲  都是提前写进沙盒过了
    NSString *pathPPT = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"578b06d152364.pptx"];
    NSString *pathPDF = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"[如何掌控自己的时间和生活].How.to.Get.Control.of.Your.Time.and.Your.Life.2006.Scan.CHS-INTERNET.pdf"];
    NSString *pathWord = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"word.doc"];
    NSString *pathPng = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"Bar.png"];
    NSString *pathXlsx = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"xlse.xlsx"];
    
    
    YHPreviewController *pre = [[YHPreviewController alloc]init];//创建对象
    
    //    [pre loadUrlPath:[NSURL fileURLWithPath:path]];//单文件预览
    
    [pre loadUrlPathList:@[[NSURL fileURLWithPath:pathWord],[NSURL fileURLWithPath:pathXlsx],[NSURL fileURLWithPath:pathPPT],[NSURL fileURLWithPath:pathPDF],[NSURL fileURLWithPath:pathPng]] andCurrentPreVItemIndex:0];//多文件预览
    
    //跳转  这里也可以使用push 自带光环 导航栏
    [self presentViewController:pre animated:YES completion:nil];
}

ok 到这里 就结束了 第三种文件打开的方式

其实,个人感觉第二种与第三种其实是一种 第二种方式UIDocumentInteractionController 内部就是对QLPreviewController的封装
上一张图片 使用UIDocumentInteractionController打开预览后 的图层结构 看看是不是有点眼熟


873B67F1-66DE-4CB1-BCF3-3E6056329E41.png

所以具体使用哪种 就看自己怎么选了

一些讨论,关于预览视图的自定义:说说预览视图 导航栏的设置 与 底部bar的设置
直接设置并没有什么卵用 苹果已经禁用掉了
我用的方式是 通过视图的外观代理类 进行获取 然后设置的 亲测有效

#pragma mark -- 设置导航栏和底部标签栏
- (void)setNavgationBarAndTabBar {
    //获取导航栏
    UINavigationBar *navBar = [UINavigationBar appearanceWhenContainedIn:[QLPreviewController class], nil];//这个方法的意思是 在QLPreviewController 这个类中获取 UINavigationBar类型的对象  这是一个局部的    还有一个全局的 获取所有类中UINavigationBar类型的对象   值得注意的是 这个方法在iOS9之后就不能用的  点进去看  会有提示该使用哪个方法
    [navBar setBarTintColor:[UIColor whiteColor]];//导航栏颜色
    [navBar setTintColor:[UIColor blackColor]];//按钮颜色
    [navBar setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor blackColor]}];//标题颜色
    
    //获取标签栏
    UIToolbar *toolBar = [UIToolbar appearanceWhenContainedIn:[QLPreviewController class], nil];
    //    [toolBar setBackgroundImage:[UIImage imageNamed:@"Bar"] forToolbarPosition:UIBarPositionBottom barMetrics:UIBarMetricsDefault];
    [toolBar setTintColor:[UIColor blackColor]];
//这里关于toolBar  为什么不使用 setBarTintColor 进行颜色的设置  这里有一个问题 是能设置颜色  但是是底层toolBar的颜色  表层的并没有设置上     但是使用图片的方式却可以设置上    
//看下图 
}
66CFECB7-D6B1-4A0B-91CF-93284F5E1592.png

设置后的 导航栏 与 底部Bar颜色都变了 证明是有效的


DC2A1077-D765-4818-B330-9663E3E5F641.png

关于UIAPPearance


508BC46C-0908-4D04-9539-797E06112F6F.png

7786798A-C1E7-4F1E-8B68-3BE043CC31F0.png
补充:消除导航栏与标签栏
思路:将加载出来的预览视图抽离出来  加载到一个新的控制器上面
此思路并非原创 在哪里看到的忘了......
@implementation PHViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    self.edgesForExtendedLayout = UIRectEdgeNone;
    //文件写入沙盒
    NSString *path = [[NSBundle mainBundle] pathForResource:@"word" ofType:@"doc"];
    NSData *dataWord = [NSData dataWithContentsOfFile:path];
    BOOL resultWord = [dataWord writeToFile:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"word.doc"] atomically:YES];
    if (resultWord) {
        NSLog(@"写入成功");
    }
    [self navigationBar];
}
- (void)navigationBar {
    self.navigationItem.title = @"文章浏览";
    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"完成" style:UIBarButtonItemStyleDone target:self action:@selector(done)];
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"查看文章" style:UIBarButtonItemStyleDone target:self action:@selector(preview)];
}
- (void)done {
    [self dismissViewControllerAnimated:YES completion:nil];
}
- (void)preview {
    NSString *pathWord = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"word.doc"];
    YHPreviewController *pre = [[YHPreviewController alloc]init];//创建对象
    [pre loadUrlPath:[NSURL fileURLWithPath:pathWord]];//单文件预览
    [self addChildViewController:pre];
    pre.view.frame = CGRectMake(0, -44, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - self.navigationController.navigationBar.bounds.size.height - [[UIApplication sharedApplication] statusBarFrame].size.height);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //延迟一秒显示 文件貌似需要调整
        [self.view addSubview:pre.view];//视图抽离与添加
    });
}
核心代码:
- (void)preview {
//1.加载需要预览的文件
    NSString *pathWord = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"word.doc"];
    YHPreviewController *pre = [[YHPreviewController alloc]init];//创建对象
    [pre loadUrlPath:[NSURL fileURLWithPath:pathWord]];//单文件预览
    [self addChildViewController:pre];//这里是为了不让预览控制器释放

2.给预览控制器视图设置frame
    pre.view.frame = CGRectMake(0, -44, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - self.navigationController.navigationBar.bounds.size.height - [[UIApplication sharedApplication] statusBarFrame].size.height);

3.延迟一秒将预览视图添加到当前控制器上面   在测试的时候加载预览视图的时候 视图会有错位 感觉上是没有加载完成  所以延迟一秒执行
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //延迟一秒显示 文件貌似需要调整
        [self.view addSubview:pre.view];//视图抽离与添加
    });
}

demo:https://github.com/DeepSeaGhost/openFileDemo
demo注意点:所有文件都没有 所以运行之前需要导入文件 文件名随意 注意代码同步

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

推荐阅读更多精彩内容