web-dev-qa-db-ja.com

iPhone / iPad / iO用の高速で無駄のないPDF Viewer-ヒントとヒント

最近PDFの描画について多くの質問がありました。

はい、UIWebViewを使用してPDFを非常に簡単にレンダリングできますが、これは優れたPDFビューアーに期待されるパフォーマンスと機能を提供できません。

PDFページを描画できます CALayerへ または IImageへ 。 Appleは、大きなPDF Zoomable UIScrollview を描画する方法を示すサンプルコードも用意しています。

しかし、同じ問題が引き続き発生します。

UIImageメソッド:

  1. UIImage内のPDFは、レイヤーアプローチと同様に光学的にスケーリングしません。
  2. CPUとメモリは、UIImagesからPDFcontextを生成するときにヒットし、それを使用して新しいズームレベルのリアルタイムレンダリングを作成することを制限/防止します。

CATiledLayerメソッド:

  1. PDFページ全体をCALayerに描画するかなりのオーバーヘッド(時間)があります:個々のタイルのレンダリングを見ることができます(tileSize Tweakでも)
  2. CALayersは事前に準備できません(画面外でレンダリング)。

一般に、PDFビューアもメモリをかなり使用します。 Appleのズーム可能なPDFの例のメモリ使用量を監視することもできます。

私の現在のプロジェクトでは、PDFビューアーを開発しており、ページのUIImageを別のスレッド(ここでも問題です!)でレンダリングし、スケールがx1のときにそれを表示しています。スケールが1を超えると、CATiledLayerレンダリングが開始されます。 iBooksは同様のダブルテイクアプローチを採用しています。ページをスクロールすると、鮮明なバージョンが表示される前に1秒以内にページの低解像度バージョンが表示されます。

PDFイメージが描画を開始する前にレイヤーをマスクする準備ができるように、ページの両側に2ページをレンダリングします。ページは、フォーカスされたページから+2ページ離れると再び破棄されます。

Drawing PDFのパフォーマンス/メモリ処理を改善するためにどんなに小さくても明白であっても、誰にも洞察がありますか?またはここで説明されている他の問題はありますか?

EDIT:いくつかのヒント(Credit- Luke Mcneice、VdesmedT、Matt Gallagher、Johann):

  • 可能な場合は、メディアをディスクに保存します。

  • TiledLayersでレンダリングする場合は、より大きなtileSizesを使用します

  • 頻繁に使用される配列をプレースホルダーオブジェクトで初期化しますが、別の設計アプローチとして this one

  • 画像はCGPDFPageRefよりも速くレンダリングされることに注意してください

  • NSOperationsまたはGCD& Blocks を使用して、事前にページを準備します。

  • 描画中のメモリ使用量を削減するために、CGContextDrawPDFPageの前にCGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);を呼び出します

  • NSOperationsをdocRefで初期化するのは悪い考えです(メモリ)。docRefをシングルトンにラップします。

  • 不要なNSOperationsをキャンセルします。特にメモリを使用する場合は、コンテキストを開いたままにしてください。

  • ページオブジェクトをリサイクルし、未使用のビューを破棄する

  • 開いているコンテキストは、不要になったらすぐに閉じてください

  • メモリ警告を受信すると、DocRefおよびすべてのページキャッシュを解放してリロードします

その他PDF機能:

ドキュメント

サンプルプロジェクト

  • Apple/ZoomingPDF -ズーム、UIScrollViewCATiledLayer
  • vfr/reader -ズーム、ページング、UIScrollViewCATiledView
  • brow/leaves -Niceトランジションでのページング
  • / skim -思われるすべて(OSXのPDFリーダー/エディター)
365
Luke Mcneice

私は、ほぼ同じアプローチを使用して、このような種類のアプリケーションを構築しました。

  • 生成されたイメージをディスクにキャッシュし、事前に別のスレッドで常に2〜3つのイメージを生成します。
  • 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オブジェクトは、取り除く方法を見つけられなかった内部キャッシュを実行しているようです。

103
VdesmedT

シンプルで効果的なPDFビューアーの場合、必要な機能が限られているときに、QuickLookフレームワークを使用できるようになりました(iOS 4.0以降)。

最初に、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];
66

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)
}

Apple DeveloperドキュメントでPDFKitの詳細を参照してください。

6
Tamás Sengel