項目須要顯示PDF文件,因而遍尋了網絡,發現的方法如下幾種:css
1.使用UIWebView加載,沒啥說的,根據文件路徑,網絡或者本地皆可,建立一個NSURLRequest,而後用webView加載就能夠了,但僅僅能顯示文件,很low;web
2.使用UIDocumentInteractionController或QLPreviewController進行預覽——依然能夠很方便的查看PDF文檔,還有些系統自帶的方法和功能,可是這依舊不是我要的。數組
3.使用第三方—— Reader(vfr) ,功能很強大,可配置性高,遠遠超出了我須要的範疇,不能更完美了。然而,就在我高高興興的準備把它加入項目的時候,才發現這貨不能加載網絡上的PDF資源--|||因而我嘗試把網絡文件下載到了本地,寫入了沙盒cache目錄,而後仍是不能加載……使勁的找了一下,彷佛應該寫到沙盒的doc目錄,因而又把文件下載到了doc目錄,而後依舊不能加載%><%好吧,只好本身動手了。(有兄弟姐妹成功使用過,萬望指教)網絡
4.本身寫,固然這也是這篇文章要寫的,功能包含:獲取網絡/本地PDF文件並顯示,能夠實現放大或縮小顯示的效果。ide
如下上貨:函數
項目思路(這很重要,只抄代碼天然是能實現功能的,可是……):佈局
第一步,天然是獲取pdf資源。Bundle中的資源就不說了,若是你的PDF文件都是存在於Bundle中的,那麼使用 Reader(vfr) 是最好的了,咱們這裏只說網絡和本地資源(好吧,本地資源和網絡資源的區別很簡單的說就在於他們的url地址不一樣,因此,咱們只說網絡資源吧,本地資源,對應替換url地址就行了)字體
下方的方法會經過一個url字符串,返回一個CGPDFDocumentRef格式的數據,你可能看到這不是一個OC方法的寫法,是的,它不是。atom
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,固然,你能夠在這裏添加一些打印日誌,方便你發現問題 } }
第二步,將獲取到的數據顯示出來,好吧,這裏就不是一句話能搞定的了,咱們須要細說。url
首先,咱們獲取到的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
而後……而後就沒有而後了,功能實現了,若是還須要其它功能,能夠再此基礎上添加