适用于iPhone / iPad / iOs的快速精益PDF阅读器 - 提示和提示?

时间:2010-10-08 10:23:10

标签: ios pdf calayer

最近有很多关于绘制PDF的问题。

是的,您可以使用UIWebView轻松呈现PDF,但这无法提供您期望从优质PDF查看器获得的性能和功能。

您可以绘制PDF页面to a CALayer to a UIImage。 Apple甚至有示例代码来展示如何绘制大型PDF in a Zoomable UIScrollview

但同样的问题不断出现。

UIImage方法:

  1. UIImage中的PDF不是光学的 规模以及层方法。
  2. CPU和内存在生成时受到影响 来自UIImages的{​​{1}} 限制/阻止使用它来创建 实时渲染新的缩放级别。
  3. CATiledLayer方法:

    1. Theres一个重要的开销(时间) 将完整的PDF页面绘制到PDFcontext:可以看到单个切片渲染(即使使用tileSize调整)
    2. CALayer无法提前做好准备     时间(在屏幕外呈现)。
    3. 一般来说,PDF查看器的内存也很重要。甚至监视苹果可缩放PDF示例的内存使用情况。

      在我目前的项目中,我正在开发一个PDF查看器,并在一个单独的线程中呈现页面的CALayers(此处也是问题!)并在比例为x1时呈现它。一旦比例为> 1,UIImage渲染就会开始。 iBooks采用了类似的双重拍摄方法,就好像你滚动页面一样,你可以在不到一秒的时间内看到页面的低分辨率版本,然后出现一个清晰的版本。

      我在页面的每一边渲染2页,使得PDF图像准备好在图层开始绘制之前对其进行遮罩。当页面离焦点页面+2页时会再次被破坏。

      是否有人有任何见解,无论多么小或明显改善Drawing PDF的性能/内存处理?或者此处讨论的任何其他问题?

      编辑: 一些提示(来自Luke Mcneice,VdesmedT,Matt Gallagher,Johann):

      • 尽可能将所有媒体保存到磁盘。

      • 如果在TiledLayers上展示

      • ,请使用更大的tileSizes
      • init经常使用的占位符对象数组,另外另一种设计方法是this one

      • 请注意,图片的渲染速度会快于CATiledLayer

      • 使用CGPDFPageRef或GCD& Blocks提前准备页面 时间。

      • NSOperations之前调用CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);以减少绘制时的内存使用量

      • 使用docRef初始化CGContextDrawPDFPage是一个坏主意(内存),将docRef包装成单例。

      • 取消不必要NSOperations当你可以,特别是如果他们将使用记忆,请注意保持上下文打开!

      • 回收页面对象并销毁未使用的视图

      • 一旦您不需要,请关闭所有打开的上下文

      • 接收内存警告时释放并重新加载DocRef和任何页面缓存

      其他PDF功能:

      文档

      示例项目

4 个答案:

答案 0 :(得分:103)

我使用近似相同的方法构建了这种类型的应用程序,除了:

  • 我将生成的图像缓存在磁盘上,并且总是在一个单独的线程中预先生成两到三个图像。
  • 我没有用UIImage覆盖,而是在缩放为1时在图层中绘制图像。当发出内存警告时,这些图块将自动释放。

每当用户开始缩放时,我获取CGPDFPage并使用适当的CTM渲染它。 - (void)drawLayer: (CALayer*)layer inContext: (CGContextRef) context中的代码如下:

CGAffineTransform currentCTM = CGContextGetCTM(context);    
if (currentCTM.a == 1.0 && baseImage) {
    //Calculate ideal scale
    CGFloat scaleForWidth = baseImage.size.width/self.bounds.size.width;
    CGFloat scaleForHeight = baseImage.size.height/self.bounds.size.height; 
    CGFloat imageScaleFactor = MAX(scaleForWidth, scaleForHeight);

    CGSize imageSize = CGSizeMake(baseImage.size.width/imageScaleFactor, baseImage.size.height/imageScaleFactor);
    CGRect imageRect = CGRectMake((self.bounds.size.width-imageSize.width)/2, (self.bounds.size.height-imageSize.height)/2, imageSize.width, imageSize.height);
    CGContextDrawImage(context, imageRect, [baseImage CGImage]);
} else {
    @synchronized(issue) { 
        CGPDFPageRef pdfPage = CGPDFDocumentGetPage(issue.pdfDoc, pageIndex+1);
        pdfToPageTransform = CGPDFPageGetDrawingTransform(pdfPage, kCGPDFMediaBox, layer.bounds, 0, true);
        CGContextConcatCTM(context, pdfToPageTransform);    
        CGContextDrawPDFPage(context, pdfPage);
    }
}

问题是包含CGPDFDocumentRef的对象。我同步访问pdfDoc属性的部分,因为我发布它并在接收memoryWarnings时重新创建它。似乎CGPDFDocumentRef对象做了一些我没有找到如何摆脱的内部缓存。

答案 1 :(得分:66)

对于简单有效的PDF查看器,当您只需要有限的功能时,您现在可以(iOS 4.0+)使用QuickLook框架:

首先,您需要关联QuickLook.framework#import <QuickLook/QuickLook.h>;

然后,在viewDidLoad或任何延迟初始化方法中:

QLPreviewController *previewController = [[QLPreviewController alloc] init];
previewController.dataSource = self;
previewController.delegate = self;
previewController.currentPreviewItemIndex = indexPath.row;
[self presentModalViewController:previewController animated:YES];
[previewController release];

答案 2 :(得分:6)

iOS 11 以来,您可以使用名为 PDFKit 的原生框架来显示和操作PDF。

导入PDFKit后,您应该使用本地或远程URL初始化PDFView并将其显示在视图中。

if let url = Bundle.main.url(forResource: "example", withExtension: "pdf") {
    let pdfView = PDFView(frame: view.frame)
    pdfView.document = PDFDocument(url: url)
    view.addSubview(pdfView)
}

Read more about PDFKit in the Apple Developer documentation.

答案 3 :(得分:-2)

 CGAffineTransform currentCTM = CGContextGetCTM(context);     if
 (currentCTM.a == 1.0 && baseImage)  {
     //Calculate ideal scale
     CGFloat scaleForWidth = baseImage.size.width/self.bounds.size.width;
     CGFloat scaleForHeight = baseImage.size.height/self.bounds.size.height; 
     CGFloat imageScaleFactor = MAX(scaleForWidth, scaleForHeight);
     CGSize imageSize = CGSizeMake(baseImage.size.width/imageScaleFactor,
 baseImage.size.height/imageScaleFactor);
     CGRect imageRect = CGRectMake((self.bounds.size.width-imageSize.width)/2,
 (self.bounds.size.height-imageSize.height)/2, imageSize.width,
 imageSize.height);
     CGContextDrawImage(context, imageRect, [baseImage CGImage]); }  else  {
     @synchronized(issue)  { 
         CGPDFPageRef pdfPage = CGPDFDocumentGetPage(issue.pdfDoc, pageIndex+1);
         pdfToPageTransform = CGPDFPageGetDrawingTransform(pdfPage, kCGPDFMediaBox, layer.bounds, 0, true);
         CGContextConcatCTM(context, pdfToPageTransform);    
         CGContextDrawPDFPage(context, pdfPage);
     } }