374

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

是的,您可以使用 a 轻松渲染 PDF,UIWebView但这不能提供您期望从一个好的 PDF 查看器获得的性能和功能。

您可以将 PDF 页面绘制到 CALayerUIImageApple 甚至有示例代码来展示如何在可缩放 UIScrollview 中绘制大型 PDF

但同样的问题不断出现。

UIImage 方法:

  1. PDF 在UIImage不光学缩放以及图层方法中。
  2. CPU 和内存在生成UIImages限制PDFcontext /阻止时使用它来创建新缩放级别的实时渲染。

CATiledLayer 方法:

  1. 将完整的 PDF 页面绘制到一个显着的开销(时间)CALayer:可以看到单个图块呈现(即使使用 tileSize 调整)
  2. CALayers无法提前准备(渲染到屏幕外)。

一般来说,PDF 查看器的内存也很重。甚至监控苹果的可缩放 PDF 示例的内存使用情况。

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

我在焦点页面的每一侧渲染 2 页,以便 PDF 图像在开始绘制之前准备好遮盖图层。当页面距离焦点页面 +2 页时,它们会再次被销毁。

有没有人有任何见解,无论改善绘图 PDF 的性能/内存处理多么小或多么明显?或这里讨论的任何其他问题?

编辑:一些提示(信用 - Luke Mcneice,VdesmedT,Matt Gallagher,Johann):

  • 尽可能将任何媒体保存到磁盘。

  • 如果在 TiledLayers 上渲染,请使用更大的 tileSizes

  • 用占位符对象初始化经常使用的数组,或者另一种设计方法是这个

  • 请注意,图像的渲染速度比CGPDFPageRef

  • 使用NSOperations或 GCD & Blocks提前准备页面。

  • 在绘图时调用CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);beforeCGContextDrawPDFPage以减少内存使用

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

  • 不必要的取消NSOperations如果可以,尤其是当他们将使用内存时,请注意不要让上下文保持打开状态!

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

  • 不需要时立即关闭所有打开的上下文

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

其他 PDF 功能:

文档

示例项目

4

3 回答 3

103

我已经使用大致相同的方法构建了这种应用程序,除了:

  • 我将生成的图像缓存在磁盘上,并且总是在单独的线程中提前生成两到三个图像。
  • 当缩放为 1 时,我不使用 a 叠加,UIImage而是在图层中绘制图像。发出内存警告时,这些图块将自动释放。

每当用户开始缩放时,我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对象做了一些我没有找到如何摆脱的内部缓存。

于 2010-10-08T11:48:36.933 回答
67

对于一个简单有效的 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];
于 2011-03-21T21:25:49.830 回答
8

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

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

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

在 Apple Developer 文档中阅读有关 PDFKit 的更多信息。

于 2018-01-29T15:14:31.203 回答