iOS在APP內瀏覽word、pdf、圖片等文件

前言:在app內查看各種文件是大部分應用都會有的需求,由于項目中做了這個功能,整合了一下寫一個記錄。

蘋果給我們提供了以下5種方法,但是以下這三種方法都是基于在本地緩存中已經保存了此文件才可以查看,不然是打不開的。

1.UIWebVIew

  • 加載簡單,只能瀏覽
  • 加載出來的用戶交互性很差,無法做任何操作
  • 無法翻頁,無法做回調

2.UIDocumentInteractionController

  • 支持瀏覽不同的文件類型,如XLS文件,Word文檔文件,PDF文件
  • 實現代理方法
  • 支持使用彈框出現調用系統內的應用APP查看文件

3.QLPreviewController

  • 支持瀏覽不同的文件類型,如XLS文件,Word文檔文件,PDF文件
  • 需導入QuickLook.framework框架,實現協議中的兩個代理方法
  • 上下滑動支持單個文檔的瀏覽,左右滑動支持不同文檔間的切換
  • 支持蘋果自帶的分享打印等。

4.用drawRect的CGContextDrawPDFPage方法直接描繪出內容來

  • 節省內存

5.第三方框架vfr/Reader加載pdf文檔

  • 集成了打印,分享,發郵件,預覽等多種功能

獲取文件方式

//本地文件
NSURL *filePath = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"xxxxxx" ofType:@"pdf"]];
//網絡文件
NSURL *filePath = [NSURL URLWithString:@"https://www.tutorialspoint.com/ios/ios_tutorial.pdf"];
UIWebView加載本地或者網絡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];

//實現代理,繼承<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 = @"附件預覽";
    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加載本地文檔

導入#import <QuickLook/QuickLook.h>

QLPreviewController *qlVC = [[QLPreviewController alloc]init];
qlVC.delegate = self;
qlVC.dataSource = self;
[self.navigationController pushViewController:qlVC animated:YES];

//實現代理<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加載網絡文檔
  • 這里地址為打開就可以直接瀏覽
  • 先判斷緩存是否存在文件
  • 存在文件直接打開(這里選擇用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加載網絡文檔
  • 這里地址為打開下載后瀏覽
  • 先判斷緩存是否存在文件
  • 存在文件直接打開(這里選擇用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 - 下載
//獲取到服務器響應
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    totaLen = response.expectedContentLength;
}

//獲取到數據流
-(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);//在這邊可以加個進度條,因為沒有導入三方就沒有加了
}

//數據請求
-(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];//清空存儲數據,這個很重要
        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);
    //上面兩句是對環境做一個仿射變換,如果不執行上面兩句那么繪制出來的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);//恢復圖形狀態
}

//通過地址字符串獲取PDF資源
CGPDFDocumentRef test(NSString*urlString) {
    NSURL*url = [NSURL URLWithString:urlString];//將傳入的字符串轉化為一個NSURL地址
    CFURLRef refURL = (__bridge_retained CFURLRef)url;//將的到的NSURL轉化為CFURLRefrefURL備用
    CGPDFDocumentRef document =CGPDFDocumentCreateWithURL(refURL);//通過CFURLRefrefURL獲取文件內容
    CFRelease(refURL);//過河拆橋,釋放使用完畢的CFURLRefrefURL,這個東西并不接受自動內存管理,所以要手動釋放
    if(document) {
//        [SVProgressHUD dismiss];
        return  document;//返回獲取到的數據
    }else{
//        [SVProgressHUD dismiss];
        return   NULL; //如果沒獲取到數據,則返回NULL,當然,你可以在這里添加一些打印日志,方便你發現問題
    }
}

//獲取所有需要顯示的PDF頁面
- (void)getDataArrayValue
{
    size_t totalPages = CGPDFDocumentGetNumberOfPages(_docRef);//獲取總頁數
    self.totalPage = (int)totalPages;//給全局變量賦值
    NSMutableArray*arr = [NSMutableArray new];
    //通過循環創建需要顯示的PDF頁面,并把這些頁面添加到數組中
    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;//給數據數組賦值
}
第三方框架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因為PDF閱讀器可能是push出來的,也可能是present出來的,為了更好的效果,這個代理方法可以實現很好的退出
- (void)dismissReaderViewController:(ReaderViewController *)viewController{
    [self dismissViewControllerAnimated:YES completion:nil];
}

代碼下載:https://github.com/valychen/OpenFile-master-
滿意的話給個star喲

感謝以下文章

iOS開發之pdf文檔的加載與瀏覽的4種方式

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容