当前位置: 首页 > 编程日记 > 正文

iOS 开发之 pdf 文档的加载与浏览的 4 种方式

原文链接:http://www.jianshu.com/p/1d4305a02ea5
在我们的开发中,有些像电子书类型的 app 的开发会涉及到 pdf 文档的加载与展示。由于笔者项目中正好涉及到这块,于是将 pdf 常用的几种加载方式做个总结。以供后面可能用到的同学做个参考。 —— 由膜法小编分享

前言

在我们的开发中,有些像电子书类型的app的开发会涉及到pdf文档的加载与展示。由于笔者项目中正好涉及到这块,于是将pdf常用的几种加载方式做个总结。以供后面可能用到的同学做个参考。

正文

通常我们用到的pdf文档的加载方式有4种:

  • UIWebView加载本地或者网络pdf文档
  • QLPreviewController加载pdf文档
  • 用CGContext画pdf文档,并结合UIPageViewController展示
  • 第三方框架vfr/Reader加载pdf文档

下面就按照上面4种方式的顺序依次介绍具体的用法。

UIWebView加载本地或者网络pdf文档

UIWebView加载pdf文档比较简单,加载本地文档和网络文档用法几乎差不多。
浏览方式是上下拖动,支持放大缩小,以及选择copy等。
加载本地文档:

    //初始化myWebViewUIWebView *myWebView = [[UIWebView alloc] init];myWebView.backgroundColor = [UIColor whiteColor];NSURL *filePath = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"myHome" ofType:@"pdf"]];NSURLRequest *request = [NSURLRequest requestWithURL: filePath];[myWebView loadRequest:request];//使文档的显示范围适合UIWebView的bounds[myWebView setScalesPageToFit:YES];

加载网络文档:

//初始化myWebViewUIWebView *myWebView = [[UIWebView alloc] init];myWebView.backgroundColor = [UIColor whiteColor];NSURL *filePath = [NSURL URLWithString:@"https://www.tutorialspoint.com/ios/ios_tutorial.pdf"];NSURLRequest *request = [NSURLRequest requestWithURL: filePath];[myWebView loadRequest:request];//使文档的显示范围适合UIWebView的bounds[myWebView setScalesPageToFit:YES];
QLPreviewController加载pdf文档

在iOS 4 SDK之后苹果退出了QLPreviewControllerAPI,组件允许用户浏览许多不同的文件类型,如XLS文件,Word文档文件,PDF文件等,但是使用此API之前用户必须导入QuickLook.framework框架,使用的QLPreviewController时,你必须实现此协议QLPreviewControllerDataSource的两个代理方法。
上下滑动支持单个文档的浏览,左右滑动支持不同文档间的切换,还支持苹果自带的分享打印等。
QLPreviewControllerDataSource的两个代理方法:

/**所要加载pdf文档的个数*/
- (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller;/** 返回每个index pdf文档所对应的文档路径*/
- (id <QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index;

QLPreviewController加载pdf文档

//QLPreviewController初始化,需要导入QuickLook.framework
QLPreviewController *QLPVC = [[QLPreviewController alloc] init];
QLPVC.dataSource = self;
[self presentViewController:QLPVC animated:YES completion:nil];#pragma mark QLPreviewControllerDataSource
- (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller{return 2;
}
- (id<QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index{NSArray *arr = @[FILE_PATH,FILE_PATH1];return [NSURL fileURLWithPath:arr[index]];
}
用CGContext画pdf文档,并结合UIPageViewController展示

首先将pdf单页的文档画在UIView的画布上:

//CFURLRef pdfURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("test.pdf"), NULL, NULL);CFURLRef pdfURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), (__bridge CFStringRef)self.fileName, NULL, NULL);
//创建CGPDFDocument对象
CGPDFDocumentRef pdfDocument = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);//获取当前的上下文
CGContextRef *context = UIGraphicsGetCurrentContext();
//Quartz坐标系和UIView坐标系不一样所致,调整坐标系,使pdf正立
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);//获取指定页的pdf文档
CGPDFPageRef page = CGPDFDocumentGetPage(pdfDocument, pageNO);
//创建一个仿射变换,该变换基于将PDF页的BOX映射到指定的矩形中。
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFCropBox, self.bounds, 0, true);
CGContextConcatCTM(context, pdfTransform);
//将pdf绘制到上下文中
CGContextDrawPDFPage(context, page);

用UIPageViewController展示分页的pdf文档

//初始化PDFPageModel
pdfPageModel = [[CGContextDrawPDFPageModel alloc] initWithPDFDocument:pdfDocument];// UIPageViewControllerSpineLocationMin 单页显示    
NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger: UIPageViewControllerSpineLocationMin]forKey: UIPageViewControllerOptionSpineLocationKey];//初始化UIPageViewController,UIPageViewControllerTransitionStylePageCurl翻页效果,UIPageViewControllerNavigationOrientationHorizontal水平方向翻页
pageViewCtrl = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl                                              navigationOrientation:UIPageViewControllerNavigationOrientationHorizontaloptions:options];
//承载pdf每页内容的控制器
CGContextDrawPDFPageController *initialViewController = [pdfPageModel viewControllerAtIndex:1];NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
//设置UIPageViewController的数据源[pageViewCtrl setDataSource:pdfPageModel];//pageViewCtrl.doubleSided = YES;设置正反面都有文字//设置pageViewCtrl的子控制器 [pageViewCtrl setViewControllers:viewControllersdirection:UIPageViewControllerNavigationDirectionReverseanimated:NOcompletion:^(BOOL f){}];[self addChildViewController:pageViewCtrl];[self.view addSubview:pageViewCtrl.view];//当我们向我们的视图控制器容器(就是父视图控制器,它调用addChildViewController方法加入子视图控制器,它就成为了视图控制器的容器)中添加(或者删除)子视图控制器后,必须调用该方法,告诉iOS,已经完成添加(或删除)子控制器的操作。[pageViewCtrl didMoveToParentViewController:self];//CGContextDrawPDFPageModel.m
//获得pdfDocument的总页数
long pageSum = CGPDFDocumentGetNumberOfPages(pdfDocument);#pragma mark返回pageViewController当前页前一页的代理方法(如果要每页的背面显示与正面相同的风格,而不是默认的白,需要设置pageController的doubleSide属性为YES,同时在下面的两个代理方法中设置BackViewController)
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {NSUInteger index = [self indexOfViewController: (CGContextDrawPDFPageController *)viewController];if ((index == 1) || (index == NSNotFound)) {return nil;}index--;return [self viewControllerAtIndex:index];
}
#pragma mark返回pageViewController当前页后一页的代理方法
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {NSUInteger index = [self indexOfViewController: (CGContextDrawPDFPageController *)viewController];if (index == NSNotFound) {return nil;}index++;//获取pdf文档的页数long pageSum = CGPDFDocumentGetNumberOfPages(pdfDocument);if (index >= pageSum+1) {return nil;}return [self viewControllerAtIndex:index];
}

也许我们在平时会注意到,一些电子书阅读器的翻页过程中会有白天模式和夜间模式,而UIPageViewController默认的翻页效果如下:


翻页默认效果.png

如果黑夜模式也是这种默认的效果如图就会很尴尬:


黑夜模式.jpeg

为了解决这种问题:
需要将UIPageViewController的doubleSided属性设为YES,然后将当前视图截图放在每页的背面这样翻页的过程中背面的效果就和相应的模式对应了。
主要修改两个地方:
第一:为设置背面的视图新建一个控制器,同时在控制器上加载一个UIImageView,图片设置为图书当前页的截图,具体实现如下:

- (void)updateWithViewController:(UIViewController *)viewController {self.backgroundImage = [self captureView:viewController.view];
}- (UIImage *)captureView:(UIView *)view {CGRect rect = view.bounds;UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0.0f);CGContextRef context = UIGraphicsGetCurrentContext();CGAffineTransform transform = CGAffineTransformMake(-1.0, 0.0, 0.0, 1.0, rect.size.width, 0.0);CGContextConcatCTM(context,transform);[view.layer renderInContext:context];UIImage *image = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();return image;
}

第二:在UIPageViewController的dataSource的代理方法中,设置背页为放截图的控制器。

#pragma mark如果要每页的背面显示与正面相同的风格,而不是默认的白,需要设置pageController的doubleSide属性为YES,同时在下面的两个代理方法中设置BackViewController
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {if([viewController isKindOfClass:[CGContextDrawPDFPageController class]]) {self.currentViewController = viewController;BackViewController *backViewController = [[BackViewController alloc] init];[backViewController updateWithViewController:viewController];return backViewController;}//self.currentViewController保存的是后一个CGContextDrawPDFPageController,如果直接用viewController实际指的是backViewController,而其没有indexOfViewController:等方法程序会崩掉。NSUInteger index = [self indexOfViewController: (CGContextDrawPDFPageController *)self.currentViewController];if ((index == 1) || (index == NSNotFound)) {return nil;}index--;return [self viewControllerAtIndex:index];
}- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {if([viewController isKindOfClass:[CGContextDrawPDFPageController class]]) {self.currentViewController = viewController;BackViewController *backViewController = [[BackViewController alloc] init];[backViewController updateWithViewController:viewController];return backViewController;}//self.currentViewController保存的是前一个CGContextDrawPDFPageController,如果直接用viewController实际指的是backViewController,而其没有indexOfViewController:等方法程序会崩掉。NSUInteger index = [self indexOfViewController: (CGContextDrawPDFPageController *)self.currentViewController];if (index == NSNotFound) {return nil;}index++;//获取pdf文档的页数long pageSum = CGPDFDocumentGetNumberOfPages(pdfDocument);if (index >= pageSum+1) {return nil;}return [self viewControllerAtIndex:index];
}
第三方框架vfr/Reader加载pdf文档

使用第三方框架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];
}

源码已上传至fenglinyunshi-git,欢迎下载,并提出宝贵意见。

结语

加载pdf文件可能还有更多的实现方式,欢迎补充,如有不准确的地方还望指正,谢谢。

问渠那得清如许,为有源头活水来。

相关文章:

利用三个点(trsf)来实现各种规则图形的实现

在Val3,是使用trsf(x,y,z,rx,ry,rz)来实现三维空间点的位置与方向。其中第一点和第二点位置很重要&#xff0c;第三点是用来确定方向。根据这三个点先确定一个用户坐标系。在这个坐标系中&#xff0c;实现圆&#xff0c;三角形&#xff0c;矩形&#xff0c;腰圆&#xff0c;正五…

OpenStack环境搭建(二:Fuel Master的安装及配置)

实验要求&#xff1a; 完成Virtual box平台安装&#xff0c;会应用相关操作&#xff1b;在virtual box虚拟平台上部署Fuel Master节点&#xff1b;在virtual box虚拟平台上部署计算节点Computer&#xff1b;在virtual box虚拟平台上部署控制节点Controller&#xff1b;在web控…

ScratchCardView:刮刮卡视图组件

原文链接&#xff1a;https://github.com/pgorzelany/ScratchCardViewScratchCardView&#xff1a;刮刮卡视图组件。# 为开源点赞# —— 由SwiftLanguage分享Example To run the example project, clone the repo, and run pod install from the Example directory first. Inst…

getaddrinfo()函数详解

1. 概述IPv4中使用gethostbyname()函数完成主机名到地址解析&#xff0c;这个函数仅仅支持IPv4&#xff0c;且不允许调用者指定所需地址类型的任何信息&#xff0c;返回的结构只包含了用于存储IPv4地址的空间。IPv6中引入了getaddrinfo()的新API&#xff0c;它是协议无关的&…

OpenStack环境搭建(三:Computer与Controller节点的安装及配置)

实验要求&#xff1a; 完成Virtual box平台安装&#xff0c;会应用相关操作&#xff1b;在virtual box虚拟平台上部署Fuel Master节点&#xff1b;在virtual box虚拟平台上部署计算节点Computer&#xff1b;在virtual box虚拟平台上部署控制节点Controller&#xff1b;在web控…

iOS - 支持水平 / 垂直显示自动滚动的跑马灯控件 --- SKAutoScrollLabel 的使用和实现

原文链接&#xff1a;http://www.jianshu.com/p/7221bc08f26aSKAutoScrollLabel 是一个同时支持水平 / 垂直两种类型的 “跑马灯” 效果的自动滚动 UILabel。在滚动的边缘使用了梯度褪色来解决滚动边缘生硬的效果问题&#xff0c;总体效果呈现出混然天成的感觉&#xff0c;并且…

Git简明手册

文&#xff0f;AbnerKang&#xff08;简书作者&#xff09;原 文链接&#xff1a;http://www.jianshu.com/p/d7a7ba4f2341?utm_campaignmaleskine& utm_contentnote&utm_mediumreader_share&utm_sourceweixin&fromtimeline&isappinstalled0著作权归作者所…

今天是2013年06月21日,博客之路开始了!

RT&#xff0c;从今天开始&#xff0c;准备养成写博客的习惯。 一是对学习的成果进行总结  二是检验自己是否真的弄懂某一个问题  三是分享出来与大家共勉。从此开启我的程序人生之路吧&#xff01;转载于:https://www.cnblogs.com/penghongwei/p/3147999.html

OpenStack环境搭建(四:web控制端各节点的部署及配置)

实验要求&#xff1a; 完成Virtual box平台安装&#xff0c;会应用相关操作&#xff1b;在virtual box虚拟平台上部署Fuel Master节点&#xff1b;在virtual box虚拟平台上部署计算节点Computer&#xff1b;在virtual box虚拟平台上部署控制节点Controller&#xff1b;在web控…

Spring+SpringMVC+shiro+mysql(一)

SpringSpringMVCshiromysql&#xff08;一&#xff09; 最近要做个后台管理系统&#xff0c;就会设计到权限的管理控制&#xff0c;于是就想到 shiro &#xff0c;下面是自己对Springshiro的一点点理解&#xff0c;记录下来&#xff0c;一起多探讨&#xff1a; 项目结构 1. pom…

【iOS】Socket/TCP 通信 发送 NSString 字符串格式数据

Socket/TCP 原理这里就不阐述了&#xff0c;网上一搜一大堆&#xff0c;直接上关键代码。 【注】iOS 目前有非常著名的第三方库 CocoaAsyncSocket 可以使用&#xff0c;但是我们项目当时做大数据上报要求直接发送 NSString 格式数据&#xff0c;所以自己写了一个简易版 TCP 连接…

[Win]进程间通信——邮槽Mailslot

进程间通信 进程的地址空间是私有的。出于安全性的目的&#xff0c;如果一个进程不具有特殊的权限&#xff0c;是无法访问另外一个进程的内存空间的&#xff0c;也无法知道内存中保存的数据的意义。但是在一些具体的应用情况下需要多个进行相互配合&#xff0c;有时计算机用户也…

OpenStack环境搭建(五:附加项虚拟机文件备份使用)

实验要求&#xff1a; 完成Virtual box平台安装&#xff0c;会应用相关操作&#xff1b;在virtual box虚拟平台上部署Fuel Master节点&#xff1b;在virtual box虚拟平台上部署计算节点Computer&#xff1b;在virtual box虚拟平台上部署控制节点Controller&#xff1b;在web控…

IOS入门-TargetAction

创建一个UIButton 并用Target - Action来监听它的点击事件 Target -- self控制器 Action -- 具体动作&#xff0c;self控制器中的某个方法 forControlEvents:UIControlEventTouchUpInside -- 表示监听的事件 1 - (void)btnclick:(id)sender2 {3 NSLog("点击%" ,…

【iOS_Development】文件操作

原文链接&#xff1a;http://www.jianshu.com/p/c5820ab6836biOS 文件操作 —— 由anticipate_91分享NSFileManager&#xff1a;是用来管理文件系统的&#xff0c;它可以用来进行常见的文件\文件夹操作获取NSFileManager示例[NSFileManager defaultManager] 增删改查 1. 创建文…

仿人智能控制器的参数简化(已发表于《计算机测量与控制》2013年第4期)

转载于:https://www.cnblogs.com/snake-hand/p/3153313.html

OpenStack环境搭建(六:常见问题及解决方案总结)

实验要求&#xff1a; 完成Virtual box平台安装&#xff0c;会应用相关操作&#xff1b;在virtual box虚拟平台上部署Fuel Master节点&#xff1b;在virtual box虚拟平台上部署计算节点Computer&#xff1b;在virtual box虚拟平台上部署控制节点Controller&#xff1b;在web控…

工作区和暂存区

Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。 先来看名词解释。 工作区&#xff08;Working Directory&#xff09; 就是你在电脑里能看到的目录&#xff0c;比如我的learngit文件夹就是一个工作区&#xff1a; 版本库&#xff08;Repository&#xff09; 工…

Lucene类介绍

IndexWriter:lucene中最重要的的类之一&#xff0c;它主要是用来将文档加入索引&#xff0c;同时控制索引过程中的一些参数使用。 Analyzer luceneAnalyzer new StandardAnalyzer(); IndexWriter indexWriter new IndexWriter(indexDir, luceneAnalyzer, true );…

iOS 开发之沙盒机制 文件操作 (NSFielManager)

原文链接&#xff1a;http://www.jianshu.com/p/349855b5a8aeiOS APP 可以在自己的沙盒里读写文件&#xff0c;但是&#xff0c;不可以访问其他 APP 的沙盒。每一个 APP 都是一个信息孤岛&#xff0c;相互是不可以进行通信的&#xff0c;唯独可以通过 URL Scheme。沙盒里面的文…

手动部署OpenStack环境(一:Virtual Box 5.1 环境的安装及配置)

任务一、Virtual Box 5.1 环境的安装及配置 1.1、安装环境检查 1.2、创建安装目录 1.3、安装及配置 实验目的及要求 完成Virtual box平台安装&#xff0c;会应用相关操作&#xff1b;在virtual box虚拟平台上部署网络节点Network&#xff1b;在virtual box虚拟平台上部署计算…

iOS动画系列之九:实现点赞的动画及播放起伏指示器

iOS动画系列&#xff0c;共十篇。现在写到第九篇啦。感兴趣的可以通过下面的传输门进到其他几篇文章里面。 第一篇&#xff1a;iOS动画系列之一&#xff1a;通过实战学习CALayer和透视的原理。做一个带时分秒指针的时钟动画(上) 第二篇&#xff1a;iOS动画系列之二&#xff1a;…

MySql5.7环境搭建

1. 安装mysql的linux系统 [rootgrewan ~]# cat /etc/redhat-release CentOS release 6.7 (Final) [rootgrewan ~]# uname -a Linux grewan 2.6.32-573.26.1.el6.x86_64 #1 SMP Wed May 4 00:57:44 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux [rootgrewan ~]# 注意&#xff1a;l…

Toad 修改起始窗口

Toad默认窗口时Editor&#xff0c; 如果想要修改为Schema Browser可以通过以下步骤进行修改&#xff08;以Toad9.6为例&#xff09;&#xff1a; 1&#xff0c;点击菜单栏上的View下拉菜单 2&#xff0c;选择Toad Options...菜单 3&#xff0c;在打开窗口的左边功能列表中找到并…

手动部署OpenStack环境(二:CentOS6.6虚拟机的安装及配置)

任务二、CentOS 6.6虚拟机的安装及配置 2.1、安装环境检查 2.2、安装及配置controller0节点 2.3、安装及配置computer0节点 2.4、安装及配置network0节点 2.5、对各节点进行基础配置及服务安装 任务二&#xff1a;CentOS6.6虚拟机的安装及配置 2.1、安装环境检查 2.1.1在…

练习-----查询

第一步&#xff1a;建表 1 create table student #学生表2 (3 Sno varchar(20) primary key, #学号&#xff0c;主键4 Sname varchar(20) not null, #学生姓名5 Ssex varchar(20) not null, #学生性别6 Sbirthday datetime, #学生出生日期7 Class …

UIActivityViewController使用

苹果从iOS6开始&#xff0c;提供了一个活动列表视图&#xff0c;为分享和操作数据提供了一个统一的服务接口&#xff0c;通过UIActivityViewController来控制它的呈现和关闭&#xff0c;凡是继承UIActivity抽象类的子类对象都可以放在列表中呈现出来。如下图所示&#xff1a; 活…

SQL Server系统表sysobjects介绍与使用

关于SQL Server数据库的一切信息都保存在它的系统表格里。我怀疑你是否花过比较多的时间来检查系统表格&#xff0c;因为你总是忙于用户表格。但是&#xff0c;你可能需要偶尔做一点不同寻常的事&#xff0c;例如数据库所有的触发器。你可以一个一个地检查表格&#xff0c;但是…

手动部署OpenStack环境(三:OpenStack环境预配置)

任务三、OpenStack环境预配置 3.1、本地OpenStack yum源制作 任务三&#xff1a;OpenStack环境预配置 3.1、本地OpenStack yum 源制作 3.1.1、拷贝镜像文件源到本地 3.1.2、查看createrepo是否安装&#xff0c;并使用yum方法安装。 &#xff08;此操作只在controller0主机进…

Java 集合框架(二)—— ArrayList

二、数组列表 —— ArrayList 1、构造方法 ArrayList 是 Java 中的动态数组&#xff0c;底层实现就是对象数组&#xff0c;只不过数组的容量会根据情况来改变。 它有个带 int 类型参数的构造方法&#xff0c;根据传入的参数&#xff0c;扩展初始化的数组容量&#xff0c;这个方…