项目需要显示PDF文件,于是遍寻了网络,发现的方法以下几种:

1.使用UIWebView加载,没啥说的,根据文件路径,网络或者本地皆可,创建一个NSURLRequest,然后用webView加载就可以了,但仅仅能显示文件,很low;

2.使用UIDocumentInteractionController或QLPreviewController进行预览——依然可以很方便的查看PDF文档,还有些系统自带的方法和功能,但是这依旧不是我要的。

3.使用第三方—— Reader(vfr) ,功能很强大,可配置性高,远远超出了我需要的范畴,不能更完美了。然而,就在我高高兴兴的准备把它加入项目的时候,才发现这货不能加载网络上的PDF资源--|||于是我尝试把网络文件下载到了本地,写入了沙盒cache目录,然后还是不能加载……使劲的找了一下,似乎应该写到沙盒的doc目录,于是又把文件下载到了doc目录,然后依旧不能加载%><%好吧,只好自己动手了。(有兄弟姐妹成功使用过,万望指教)

4.自己写,当然这也是这篇文章要写的,功能包含:获取网络/本地PDF文件并显示,可以实现放大或缩小显示的效果。

以下上货:

项目思路(这很重要,只抄代码自然是能实现功能的,但是……):

第一步,自然是获取pdf资源。Bundle中的资源就不说了,如果你的PDF文件都是存在于Bundle中的,那么使用 Reader(vfr) 是最好的了,我们这里只说网络和本地资源(好吧,本地资源和网络资源的区别很简单的说就在于他们的url地址不同,所以,我们只说网络资源吧,本地资源,对应替换url地址就好了)

下方的方法会通过一个url字符串,返回一个CGPDFDocumentRef格式的数据,你可能看到这不是一个OC方法的写法,是的,它不是。

CGPDFDocumentRef   test(NSString*urlString) {
NSURL*url = [NSURLURLWithString:urlString];//将传入的字符串转化为一个NSURL地址
CFURLRefrefURL = (__bridge_retainedCFURLRef)url;//将的到的NSURL转化为CFURLRefrefURL备用
CGPDFDocumentRefdocument =CGPDFDocumentCreateWithURL(refURL);//通过CFURLRefrefURL获取文件内容
CFRelease(refURL);//过河拆桥,释放使用完毕的CFURLRefrefURL,这个东西并不接受自动内存管理,所以要手动释放
if(document) {
return document;//返回获取到的数据
}else{
return NULL; //如果没获取到数据,则返回NULL,当然,你可以在这里添加一些打印日志,方便你发现问题
}
}

第二步,将获取到的数据显示出来,好吧,这里就不是一句话能搞定的了,我们需要细说。

首先,我们获取到的PDF资源十有八九不仅仅是一页,而是很多页,所以肯定不可能在同一个视图上显示。那么我们就需要单独的获取到PDF资源数据中某一页的数据,别慌,系统有专门的函数;然后我们大概还需要知道这个PDF资源一共有多少页,别慌,这个系统也有专门的函数,我们会在用到的时候说明。

其次,获取到某一页的数据后,我们还要把它展示到一个view上面,最后很多个view 的集合就是我们需要展示的所有东西了。

所以,我们需要自定义一个view,然后传入我们通过上面方法已经获取到的CGPDFDocumentRef数据和需要显示的页数,让这个view来展示对应页数的PDF文件内容。(为什么需要一个view,因为这里会用到 Quartz 2D绘图,需要重写view的- (void)drawRect:(CGRect)rect方法来实现PDF文件内容的绘制)

那么下一步自然是新建一个view,继承自UIView,这里我取名为RiderPDFView,以下为.h文件内容:

#import <UIKit/UIKit.h>
@interface RiderPDFView :UIView
//写一个方法,通过Frame、已经获取到的CGPDFDocumentRef文件和需要显示的PDF文件的页码,来创建一个显示PDF文件内容的视图
- (instancetype)initWithFrame:(CGRect)frame documentRef:(CGPDFDocumentRef)docRef andPageNum:(int)page;
@end

然后是自定义的视图中.m文件的内容

#import "RiderPDFView.h"
@interface RiderPDFView() {
CGPDFDocumentRef documentRef;//用它来记录传递进来的PDF资源数据
int pageNum;//记录需要显示页码
}
@end
@implementation RiderPDFView
//这个方法就不多说了……
- (instancetype)initWithFrame:(CGRect)frame documentRef:(CGPDFDocumentRef)docRef andPageNum:(int)page {
self= [superinitWithFrame:frame];
documentRef= docRef;
pageNum= page;
self.backgroundColor= [UIColorwhiteColor];
returnself;
}
//重写- (void)drawRect:(CGRect)rect方法
- (void)drawRect:(CGRect)rect {
[selfdrawPDFIncontext:UIGraphicsGetCurrentContext()];//将当前的上下文环境传递到方法中,用于绘图
}
//- (void)drawRect:(CGRect)rect具体的内容
- (void)drawPDFIncontext:(CGContextRef)context {
CGContextTranslateCTM(context,0.0,self.frame.size.height);
CGContextScaleCTM(context,1.0, -1.0);
//上面两句是对环境做一个仿射变换,如果不执行上面两句那么绘制出来的PDF文件会呈倒置效果,第二句的作用是使图形呈正立显示,第一句是调整图形的位置,如不执行绘制的图形会不在视图可见范围内
CGPDFPageRef pageRef =CGPDFDocumentGetPage(documentRef,pageNum);//获取需要绘制的页码的数据。两个参数,第一个数传递进来的PDF资源数据,第二个是传递进来的需要显示的页码
CGContextSaveGState(context);//记录当前绘制环境,防止多次绘画
CGAffineTransform pdfTransForm =CGPDFPageGetDrawingTransform(pageRef,kCGPDFCropBox,self.bounds,0,true);//创建一个仿射变换的参数给函数。第一个参数是对应页数据;第二个参数是个枚举值,我每个都试了一下,貌似没什么区别……但是网上看的资料都用的我当前这个,所以就用这个了;第三个参数,是图形绘制的区域,我设置的是当前视图整个区域,如果有需要,自然是可以修改的;第四个是旋转的度数,这里不需要旋转了,所以设置为0;第5个,传递true,会保持长宽比
CGContextConcatCTM(context, pdfTransForm);//把创建的仿射变换参数和上下文环境联系起来
CGContextDrawPDFPage(context, pageRef);//把得到的指定页的PDF数据绘制到视图上
CGContextRestoreGState(context);//恢复图形状态
}

以上就是自定义的view中所有需要的东西,这里我们已经完成了PDF文件的绘制。接下来就是如何更好的展示得到的视图了。

因为得到的PDF文件可能字体很小,特别是在iPhone上面,所以你大概必不可少的需要给他添加一个放大、缩小的功能,不然直接用webVie加载不就好了,干嘛还做这么多事情。

说到放大缩小,我第一时间想到的就是用UISCrollView来做,用它可比自定义捏合手势方便多了。

我首先用了UISCrollView嵌套UISCrollView的方法,一个UISCrollView中嵌套三个UISCrollView,目的倒是达到了,但是代码量和逻辑处理都太多,而且就在做成功的那一刻,我想起了,还有个东西叫UIColectionView……于是果断弃暗投明,使用UICollectionView重做了一次,内存占用减少了1/2,所以我们这里使用UICollectionView来完成功能。你基本没用过UICollectionView?别慌,简单的要死。

为了达成我们放大缩小的功能,我们需要自定义一个UICollectionViewCell,那么我们新建一个类,继承自UICollectionViewCell,这里我命名为CollectionViewCell,以下是它.h文件中的内容:

#import <UIKit/UIKit.h>
@class CollectionViewCell;
@protocol collectionCellDelegate
@optional
- (void)collectioncellTaped:(CollectionViewCell*)cell;
@end
//上面是一个代理协议,某个CollectionViewCell被单击时候的回调,你可能是需要的,也可能不需要
@interface CollectionViewCell :UICollectionViewCell
@property(nonatomic,strong) UIScrollView *contentScrollView; //用于实现缩放功能的UISCrollView
@property(nonatomic,strong) UIView *showView;//这个就是现实PDF文件内容的视图
@property(nonatomic,weak)id <collectionCellDelegate> cellTapDelegate;//代理
@end

然后是它.m文件中的内容

#import "CollectionViewCell.h"
#import "RiderPDFView.h"
@interface CollectionViewCell()<UIScrollViewDelegate>//遵守UISCrollViewDelegate协议,这样才能实现缩放
@end
@implementation CollectionViewCell
//重写init方法
- (instancetype)initWithFrame:(CGRect)frame {
if(self= [superinitWithFrame:frame]) {
_contentScrollView= [[UIScrollView alloc]initWithFrame:self.bounds];//初始化_contentScrollView
_contentScrollView.contentSize= frame.size;//设置_contentScrollView的内容尺寸
_contentScrollView.minimumZoomScale=0.5;//设置最小缩放比例
_contentScrollView.maximumZoomScale=2.5;//设置最大的缩放比例
_contentScrollView.delegate=self;//设置代理
[self.contentView addSubview:_contentScrollView];//将_contentScrollView添加到CollectionViewCell中
UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(cellClicked)];//创建手势
[self addGestureRecognizer: tapGes];//添加手势到CollectionViewCell上
}
returnself;
}
//这是scrollView的代理方法,实现后才能通过scrollView实现缩放
- (UIView*)viewForZoomingInScrollView:(UIScrollView*)scrollView {
for(UIView*view in scrollView.subviews) {
if([view isKindOfClass:[RiderPDFView class]]) {
return view;//返回需要被缩放的视图
}
}
returnnil;
}
//重写set方法
- (void)setShowView:(UIView*)showView {
for(UIView*tempView in _contentScrollView.subviews) {
[tempView removeFromSuperview];//移除_contentScrollView中的所有视图
}
_showView= showView;赋值
[_contentScrollView addSubview:showView];//将需要显示的视图添加到_contentScrollView上
}
//tap事件
- (void)cellClicked {
if([self.cellTapDelegaterespondsToSelector:@selector(collectioncellTaped:)]) {
[self.cellTapDelegatecollectioncellTaped:self];
}
}

以上已完成了所有的准备工作,接下来就是在一个视图控制器中完整的展示获得的PDF文件了,下面是一个ViewController.m中的内容:

#import "ViewController.h"
#import "CollectionViewCell.h"//导入自定义的CollectionViewCell
#import "RiderPDFView.h"//导入展示PDF文件内容的View
@interface ViewController()<UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout,UIScrollViewDelegate,collectionCellDelegate>//遵守协议
{
UICollectionView *testCollectionView; //展示用的CollectionView
CGPDFDocumentRef _docRef; 需要获取的PDF资源文件
}
@property(nonatomic,strong)NSMutableArray*dataArray;//存数据的数组
@property(nonatomic,assign)int totalPage;//一共有多少页
@end
@implementationViewController
- (void)viewDidLoad {
[superviewDidLoad];
_docRef=test(@"http://teaching.csse.uwa.edu.au/units/CITS4401/practicals/James1_files/SPMP1.pdf");//通过test函数获取PDF文件资源,test函数的实现为我们最上面的方法,当然下面又写了一遍
[self getDataArrayValue];//获取需要展示的数据
UICollectionViewFlowLayout*layout = [[UICollectionViewFlowLayout alloc]init];//UICollectionView需要在创建的时候传入一个布局参数,故在创建它之前,先创建一个布局,这里使用系统的布局就好
layout.itemSize=self.view.frame.size;//设置CollectionView中每个item及集合视图中每单个元素的大小,我们每个视图使用一页来显示,所以设置为当前视图的大小
[layout setScrollDirection:UICollectionViewScrollDirectionHorizontal];//设置滑动方向为水平方向,也可以设置为竖直方向
layout.minimumLineSpacing=0;//设置item之间最下行距
layout.minimumInteritemSpacing=0;//设置item之间最小间距
testCollectionView= [[UICollectionView alloc]initWithFrame:self.view.bounds collectionViewLayout:layout];//创建一个集合视图,设置其大小为当前view的大小,布局为上面我们创建的布局
testCollectionView.pagingEnabled=YES;//设置集合视图一页一页的翻动
[testCollectionView registerClass:[CollectionViewCell class]forCellWithReuseIdentifier:@"test"];//为集合视图注册单元格
testCollectionView.delegate=self;//设置代理
testCollectionView.dataSource=self;//设置数据源
[self.view addSubview:testCollectionView];//将集合视图添加到当前视图上
}
- (void)didReceiveMemoryWarning {
[superdidReceiveMemoryWarning];
}
//通过地址字符串获取PDF资源
CGPDFDocumentReftest(NSString*fileName) {
NSURL*url = [NSURLURLWithString:fileName];
CFURLRefrefURL = (__bridge_retainedCFURLRef)url;
CGPDFDocumentRefdocument =CGPDFDocumentCreateWithURL(refURL);
CFRelease(refURL);
if(document) {
returndocument;
}else{
returnNULL;
}
}
//获取所有需要显示的PDF页面
- (void)getDataArrayValue {
size_t totalPages =CGPDFDocumentGetNumberOfPages(_docRef);//获取总页数
self.totalPage= (int)totalPages;//给全局变量赋值
NSMutableArray*arr = [NSMutableArray new];
//通过循环创建需要显示的PDF页面,并把这些页面添加到数组中
for(inti =1; i <= totalPages; i++) {
RiderPDFView *view = [[RiderPDFView alloc]initWithFrame:CGRectMake(0,0,self.view.frame.size.width,self.view.frame.size.height) documentRef: _docRef andPageNum:i];
[arr addObject:view];
}
self.dataArray= arr;//给数据数组赋值
}
//返回集合视图共有几个分区
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView {
return1;
}
//返回集合视图中一共有多少个元素——自然是总页数
- (NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section {
return self.totalPage;
}
//复用、返回cell
- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(NSIndexPath*)indexPath {
CollectionViewCell*cell = [collectionViewdequeueReusableCellWithReuseIdentifier:@"test"forIndexPath:indexPath];
cell.cellTapDelegate=self;//设置tap事件代理
cell.showView=self.dataArray[indexPath.row];//赋值,设置每个item中显示的内容
returncell;
}
//当集合视图的item被点击后触发的事件,根据个人需求写
- (void)collectioncellTaped:(CollectionViewCell*)cell {
NSLog(@"我点了咋的?");
}
//集合视图继承自scrollView,所以可以用scrollView 的代理事件,这里的功能是当某个item不在当前视图中显示的时候,将它的缩放比例还原
- (void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView {
for(UIView *view in testCollectionView.subviews) {
if([view isKindOfClass:[CollectionViewCell class]]) {
CollectionViewCell*cell = (CollectionViewCell*)view;
[cell.contentScrollViewsetZoomScale:1.0];
}
}
}
@end

然后……然后就没有然后了,功能实现了,如果还需要其它功能,可以再此基础上添加

PDF文件的加载及展示的更多相关文章

  1. HTML页面处理以及资源文件的加载

    Javascript 异步加载详解 这篇文章很详细的介绍了HTML的页面处理以及资源文件的加载. 本文总结一下浏览器在 javascript 的加载方式. 关键词:异步加载(async loading ...

  2. Spire.XLS,生成Excel文件、加载Excel文件

    一.组件介绍 Spire.XLS是E-iceblue开发的一套基于企业级的专业Office文档处理的组件之一,全称Spire.Office for .NET.旗下有Spire.Doc,Spire XL ...

  3. nginx反向代理转发后页面上的js css文件无法加载【原创】

    故障现象:nginx做代理转发后,发现页面上的js css文件无法加载,页面样式乱了. 原因:没有配置静态资源 解决js css文件无法加载无法访问的问题 解决办法: 修改配置文件nginx.conf ...

  4. Django---MTV和MVC的了解,Django的模版语言变量和逻辑,常见的模板语言过滤器,自定义过滤器,CSRF了解,Django的母版(继承extends,块block,组件include,静态文件的加载load static),自定义simple_tag和inclusion_tag

    Django---MTV和MVC的了解,Django的模版语言变量和逻辑,常见的模板语言过滤器,自定义过滤器,CSRF了解,Django的母版(继承extends,块block,组件include,静 ...

  5. xib文件的加载方法

    xib文件的加载方法 以UITableViewCell的cell为例 很多时候因为系统的cell无法满足我们的日常需求,我们都会自定义cell 因为cell的界面比较固定,所以通常都会选择用xib来描 ...

  6. 让Lua自己把文件夹下面的所有文件自动加载起来吧

    没有想到我也做了一回标题党.其实这里边说的自动还是有夸大其词的部分.其实只是指定文件夹,然后根据指定文件夹数据,加载目录下边的内容而已. 怎么来进行Lua文件的加载 一般情况下,相关的功能需要给他创建 ...

  7. 速战速决 (5) - PHP: 动态地创建属性和方法, 对象的复制, 对象的比较, 加载指定的文件, 自动加载类文件, 命名空间

    [源码下载] 速战速决 (5) - PHP: 动态地创建属性和方法, 对象的复制, 对象的比较, 加载指定的文件, 自动加载类文件, 命名空间 作者:webabcd 介绍速战速决 之 PHP 动态地创 ...

  8. C#遍历XML文件动态加载菜单

    通过遍历XML文件动态加载菜单,顺便利用WebBrowser控件实现一个简单的桌面浏览器 效果如下: 代码如下: XMLFile1.xml <?xml version="1.0&quo ...

  9. C#动态编译代码,执行一个代码片段,或者从指定文件中加载某个接口的实现类

    在项目进行中有时候会需要配置一些复杂的表达式,在程序运行的时候执行表达式,根据结果执行相应的操作,简单写了一个类Expression,利用.net的动态编译技术实现,代码如下: public clas ...

随机推荐

  1. 隐马尔科夫模型(Hidden Markov Models)

    链接汇总 http://www.csie.ntnu.edu.tw/~u91029/HiddenMarkovModel.html 演算法笔记 http://read.pudn.com/downloads ...

  2. linux CentOS6.5 yum安装mysql 5.6(转载&删改)

    按:下面文章经过我一路测试没有问题,是篇好文,在此感谢作者 别踩我袈裟  .另因原文有些啰嗦,我自己有所删改,并尾后增加了一大段. 出处:https://www.cnblogs.com/renjido ...

  3. NET Framework安装失败的麻烦

    本人机子环境是安装了VS2012,即安装了 .NET Framework4.5,现在要安装AutoCAD2013,而安装CAD2013需要安装4.0的Framework,由于本机已有高版本的Frame ...

  4. apache服务器多端口支持

    本示例支持80,82两端口 修改conf/httpd.conf文件: Listen 改为 Listen Listen # Virtual hosts #Include conf/extra/httpd ...

  5. RMSE均方根误差学习笔记

    1.均方根误差,它是观测值与真值偏差的平方和观测次数n比值的平方根,在实际测量中,观测次数n总是有限的,真值只能用最可信赖(最佳)值来代替.方根误差对一组测量中的特大或特小误差反映非常敏感,所以,均方 ...

  6. Shodan:黑客的物联网搜索引擎

    记得看过一个电影.里面的科学家开发了一个超级系统,能够实时监控全部可用摄像头.让逃犯无处遁形. Shodan这个新型的搜索引擎可能会让这个想法变成现实. 和Google这些传统互联网信息搜索引擎不同. ...

  7. One or more files are in a conflicted state

    http://blog.csdn.net/caiwenfeng_for_23/article/details/37501249 解决代码冲突 如果commit时出现“You have to updat ...

  8. 查看客户端的IP地址,机器名,MAC地址,登陆名等信息

    查看客户端的IP地址,机器名,MAC地址,登陆名等信息 SELECT s.session_id,s.login_time,s.host_name,p.loginame,s.program_name,c ...

  9. alarm 和 sleep

    http://blog.sina.com.cn/s/blog_6a1837e90100uhl3.html alarm也称为闹钟函数,alarm()用来设置信号SIGALRM在经过参数seconds指定 ...

  10. js&jquery避免报错的方法

      CreateTime--2016年12月8日15:28:40Author:Marydonjs&jquery规避报错信息的两种方式 <script type="text/ja ...