前言:在app內(nèi)查看各種文件是大部分應(yīng)用都會(huì)有的需求,由于項(xiàng)目中做了這個(gè)功能,整合了一下寫一個(gè)記錄。
蘋果給我們提供了以下5種方法,但是以下這三種方法都是基于在本地緩存中已經(jīng)保存了此文件才可以查看,不然是打不開的。
1.UIWebVIew
- 加載簡(jiǎn)單,只能瀏覽
- 加載出來的用戶交互性很差,無法做任何操作
- 無法翻頁,無法做回調(diào)
2.UIDocumentInteractionController
- 支持瀏覽不同的文件類型,如XLS文件,Word文檔文件,PDF文件
- 實(shí)現(xiàn)代理方法
- 支持使用彈框出現(xiàn)調(diào)用系統(tǒng)內(nèi)的應(yīng)用APP查看文件
3.QLPreviewController
- 支持瀏覽不同的文件類型,如XLS文件,Word文檔文件,PDF文件
- 需導(dǎo)入QuickLook.framework框架,實(shí)現(xiàn)協(xié)議中的兩個(gè)代理方法
- 上下滑動(dòng)支持單個(gè)文檔的瀏覽,左右滑動(dòng)支持不同文檔間的切換
- 支持蘋果自帶的分享打印等。
4.用drawRect的CGContextDrawPDFPage方法直接描繪出內(nèi)容來
- 節(jié)省內(nèi)存
5.第三方框架vfr/Reader加載pdf文檔
- 集成了打印,分享,發(fā)郵件,預(yù)覽等多種功能
獲取文件方式
//本地文件
NSURL *filePath = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"xxxxxx" ofType:@"pdf"]];
//網(wǎng)絡(luò)文件
NSURL *filePath = [NSURL URLWithString:@"https://www.tutorialspoint.com/ios/ios_tutorial.pdf"];
UIWebView加載本地或者網(wǎng)絡(luò)pdf文檔
UIWebView *webView = [[UIWebView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
webview.scalesPageToFit = YES;//使文檔的顯示范圍適合UIWebView的bounds
NSURL *filePath = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"myHome" ofType:@"pdf"]];
NSURLRequest *request = [NSURLRequest requestWithURL: filePath];
[myWebView loadRequest:request];
UIDocumentInteractionController加載本地文檔
UIDocumentInteractionController *documentInteractionController = [UIDocumentInteractionController
interactionControllerWithURL:URL];
// Configure Document Interaction Controller
documentInteractionController.delegate = self;
// Preview File
//直接打開
documentInteractionController presentPreviewAnimated:YES];
//有選擇view(直接打開和選擇view選擇其一寫)
[documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.view animated:YES];
//實(shí)現(xiàn)代理,繼承<UIDocumentInteractionControllerDelegate>
- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller{
return self;
}
// Preview presented/dismissed on document. Use to set up any HI underneath.
- (void)documentInteractionControllerWillBeginPreview:(UIDocumentInteractionController *)controller{
controller.name = @"附件預(yù)覽";
NSLog(@"willBeginPreview");
}
- (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controller{
NSLog(@"didEndPreview");
[self.navigationController popViewControllerAnimated:YES];
}
// Options menu presented/dismissed on document. Use to set up any HI underneath.
- (void)documentInteractionControllerWillPresentOptionsMenu:(UIDocumentInteractionController *)controller{
NSLog(@"willPresentOptionsMenu");
}
- (void)documentInteractionControllerDidDismissOptionsMenu:(UIDocumentInteractionController *)controller{
NSLog(@"didDismissOptionsMenu");
}
// Open in menu presented/dismissed on document. Use to set up any HI underneath.
- (void)documentInteractionControllerWillPresentOpenInMenu:(UIDocumentInteractionController *)controller{
NSLog(@"willPresentOpenInMenu");
}
- (void)documentInteractionControllerDidDismissOpenInMenu:(UIDocumentInteractionController *)controller{
NSLog(@"didDismissOpenInMenu");
[self.navigationController popViewControllerAnimated:YES];
}
QLPreviewController加載本地文檔
導(dǎo)入#import <QuickLook/QuickLook.h>
QLPreviewController *qlVC = [[QLPreviewController alloc]init];
qlVC.delegate = self;
qlVC.dataSource = self;
[self.navigationController pushViewController:qlVC animated:YES];
//實(shí)現(xiàn)代理<QLPreviewControllerDataSource,QLPreviewControllerDelegate>
#pragma mark -
- (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller {
return 1;
}
- (id <QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index {
return self.fileURL;
}
- (void)previewControllerWillDismiss:(QLPreviewController *)controller {
NSLog(@"previewControllerWillDismiss");
}
- (void)previewControllerDidDismiss:(QLPreviewController *)controller {
NSLog(@"previewControllerDidDismiss");
}
- (BOOL)previewController:(QLPreviewController *)controller shouldOpenURL:(NSURL *)url forPreviewItem:(id <QLPreviewItem>)item{
return YES;
}
- (CGRect)previewController:(QLPreviewController *)controller frameForPreviewItem:(id <QLPreviewItem>)item inSourceView:(UIView * __nullable * __nonnull)view{
return CGRectZero;
}
QLPreviewController加載網(wǎng)絡(luò)文檔
- 這里地址為打開就可以直接瀏覽
- 先判斷緩存是否存在文件
- 存在文件直接打開(這里選擇用webview打開,用其他也可以)
- 如果不存在文件先緩存在本地,再用webview加載
NSURL *targetURL = [NSURL URLWithString:self.fileURLString];
NSString *docPath = [self documentsDirectoryPath];
NSString *pathToDownloadTo = [NSString stringWithFormat:@"%@/%@", docPath, [targetURL lastPathComponent]];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL hasDownLoad= [fileManager fileExistsAtPath:pathToDownloadTo];
if (hasDownLoad) {
self.fileURL = [NSURL fileURLWithPath:pathToDownloadTo];
QLPreviewController *qlVC = [[QLPreviewController alloc]init];
qlVC.delegate = self;
qlVC.dataSource = self;
[self.navigationController pushViewController:qlVC animated:YES];
} else {
NSURL *targetURL = [NSURL URLWithString:self.fileURLString];
NSData *fileData = [[NSData alloc] initWithContentsOfURL:targetURL];
// Get the path to the App's Documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
[fileData writeToFile:[NSString stringWithFormat:@"%@/%@", documentsDirectory, [targetURL lastPathComponent]] atomically:YES];
NSURLRequest *request = [NSURLRequest requestWithURL:targetURL];
[openFileWebView loadRequest:request];
}
QLPreviewController加載網(wǎng)絡(luò)文檔
- 這里地址為打開下載后瀏覽
- 先判斷緩存是否存在文件
- 存在文件直接打開(這里選擇用QLPreviewController打開,用其他也可以)
- 如果不存在文件先下載后緩存在本地,再用QLPreviewController打開本地文檔
self.fileName = @"";
if (self.fileURLString.length > 0) {
NSRange range = [self.fileURLString rangeOfString:@"fileName="];//文件名位于的位置
//截取文件名字方便存緩存
self.fileName = [self.fileURLString substringWithRange:NSMakeRange(range.length + range.location, self.fileURLString.length - range.length - range.location)];
if ([self hasFileInApp:self.fileName]) {//存在文件,直接打開
[self pushPreView:self.fileName];
}else{//不存在文件名,去下載
[self.view addSubview:_progressV];
NSURL *targetURL = [NSURL URLWithString:self.fileURLString];
NSURLRequest *request = [NSURLRequest requestWithURL:targetURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
[NSURLConnection connectionWithRequest:request delegate:self];
}
}
#pragma mark - 下載
//獲取到服務(wù)器響應(yīng)
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
totaLen = response.expectedContentLength;
}
//獲取到數(shù)據(jù)流
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[self.fileData appendData:data];
currentLen = self.fileData.length;
self.progressV.progress = currentLen*1.0/totaLen;
NSLog(@"%f",currentLen*1.0/totaLen);//在這邊可以加個(gè)進(jìn)度條,因?yàn)闆]有導(dǎo)入三方就沒有加了
}
//數(shù)據(jù)請(qǐng)求
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
//第一次下載完存入緩存,并打開文檔
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *filePath = [docPath stringByAppendingPathComponent:self.fileName];
BOOL success = [fileManager createFileAtPath:filePath contents:self.fileData attributes:nil];
if (success == YES) {
self.fileURL = [NSURL fileURLWithPath:filePath];
self.fileData = [NSMutableData new];//清空存儲(chǔ)數(shù)據(jù),這個(gè)很重要
dispatch_async(dispatch_get_main_queue(), ^{
QLPreviewController *qlVC = [[QLPreviewController alloc]init];
qlVC.delegate = self;
qlVC.dataSource = self;
[self.navigationController pushViewController:qlVC animated:YES];
});
}
}
CGContextDrawPDFPage加載本地文檔
- 這邊只截取了重要部分代碼,全部代碼看附件
- (void)drawPDFIncontext:(CGContextRef)context
{
CGContextTranslateCTM(context,0.0,self.frame.size.height);
CGContextScaleCTM(context,1.0, -1.0);
//上面兩句是對(duì)環(huán)境做一個(gè)仿射變換,如果不執(zhí)行上面兩句那么繪制出來的PDF文件會(huì)呈倒置效果,第二句的作用是使圖形呈正立顯示,第一句是調(diào)整圖形的位置,如不執(zhí)行繪制的圖形會(huì)不在視圖可見范圍內(nèi)
CGPDFPageRef pageRef = CGPDFDocumentGetPage(documentRef,pageNum);//獲取需要繪制的頁碼的數(shù)據(jù)。兩個(gè)參數(shù),第一個(gè)數(shù)傳遞進(jìn)來的PDF資源數(shù)據(jù),第二個(gè)是傳遞進(jìn)來的需要顯示的頁碼
CGContextSaveGState(context);//記錄當(dāng)前繪制環(huán)境,防止多次繪畫
CGAffineTransform pdfTransForm = CGPDFPageGetDrawingTransform(pageRef,kCGPDFCropBox,self.bounds,0,true);//創(chuàng)建一個(gè)仿射變換的參數(shù)給函數(shù)。第一個(gè)參數(shù)是對(duì)應(yīng)頁數(shù)據(jù);第二個(gè)參數(shù)是個(gè)枚舉值,我每個(gè)都試了一下,貌似沒什么區(qū)別……但是網(wǎng)上看的資料都用的我當(dāng)前這個(gè),所以就用這個(gè)了;第三個(gè)參數(shù),是圖形繪制的區(qū)域,我設(shè)置的是當(dāng)前視圖整個(gè)區(qū)域,如果有需要,自然是可以修改的;第四個(gè)是旋轉(zhuǎn)的度數(shù),這里不需要旋轉(zhuǎn)了,所以設(shè)置為0;第5個(gè),傳遞true,會(huì)保持長(zhǎng)寬比
CGContextConcatCTM(context, pdfTransForm);//把創(chuàng)建的仿射變換參數(shù)和上下文環(huán)境聯(lián)系起來
CGContextDrawPDFPage(context, pageRef);//把得到的指定頁的PDF數(shù)據(jù)繪制到視圖上
CGContextRestoreGState(context);//恢復(fù)圖形狀態(tài)
}
//通過地址字符串獲取PDF資源
CGPDFDocumentRef test(NSString*urlString) {
NSURL*url = [NSURL URLWithString:urlString];//將傳入的字符串轉(zhuǎn)化為一個(gè)NSURL地址
CFURLRef refURL = (__bridge_retained CFURLRef)url;//將的到的NSURL轉(zhuǎn)化為CFURLRefrefURL備用
CGPDFDocumentRef document =CGPDFDocumentCreateWithURL(refURL);//通過CFURLRefrefURL獲取文件內(nèi)容
CFRelease(refURL);//過河拆橋,釋放使用完畢的CFURLRefrefURL,這個(gè)東西并不接受自動(dòng)內(nèi)存管理,所以要手動(dòng)釋放
if(document) {
// [SVProgressHUD dismiss];
return document;//返回獲取到的數(shù)據(jù)
}else{
// [SVProgressHUD dismiss];
return NULL; //如果沒獲取到數(shù)據(jù),則返回NULL,當(dāng)然,你可以在這里添加一些打印日志,方便你發(fā)現(xiàn)問題
}
}
//獲取所有需要顯示的PDF頁面
- (void)getDataArrayValue
{
size_t totalPages = CGPDFDocumentGetNumberOfPages(_docRef);//獲取總頁數(shù)
self.totalPage = (int)totalPages;//給全局變量賦值
NSMutableArray*arr = [NSMutableArray new];
//通過循環(huán)創(chuàng)建需要顯示的PDF頁面,并把這些頁面添加到數(shù)組中
for(int i =1; i <= totalPages; i++) {
ReaderPDFView *view = [[ReaderPDFView 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;//給數(shù)據(jù)數(shù)組賦值
}
第三方框架vfr/Reader加載pdf文檔
- 這部分在代碼中沒有,自行百度吧
//Reader初始化 加載本地pdf文件
ReaderDocument *doc = [[ReaderDocument alloc] initWithFilePath:FILE_PATH password:nil];
ReaderViewController *rederVC = [[ReaderViewController alloc] initWithReaderDocument:doc];
rederVC.delegate = self;
rederVC.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
rederVC.modalPresentationStyle = UIModalPresentationOverFullScreen;
[self presentViewController:rederVC animated:YES completion:nil];
#pragma mark ReaderViewControllerDelegate因?yàn)镻DF閱讀器可能是push出來的,也可能是present出來的,為了更好的效果,這個(gè)代理方法可以實(shí)現(xiàn)很好的退出
- (void)dismissReaderViewController:(ReaderViewController *)viewController{
[self dismissViewControllerAnimated:YES completion:nil];
}
代碼下載:https://github.com/valychen/OpenFile-master-
滿意的話給個(gè)star喲