最近PDFの描画について多くの質問がありました。
はい、UIWebView
を使用してPDFを非常に簡単にレンダリングできますが、これは優れたPDFビューアーに期待されるパフォーマンスと機能を提供できません。
PDFページを描画できます CALayerへ または IImageへ 。 Appleは、大きなPDF Zoomable UIScrollview を描画する方法を示すサンプルコードも用意しています。
しかし、同じ問題が引き続き発生します。
UIImageメソッド:
UIImage
内のPDFは、レイヤーアプローチと同様に光学的にスケーリングしません。UIImages
からPDFcontext
を生成するときにヒットし、それを使用して新しいズームレベルのリアルタイムレンダリングを作成することを制限/防止します。CATiledLayerメソッド:
CALayer
に描画するかなりのオーバーヘッド(時間)があります:個々のタイルのレンダリングを見ることができます(tileSize Tweakでも)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機能:
PDF内のリンクの取得 (および here および here )
リンクのターゲットを取得 (/Dest
配列からページ番号を取得)
ドキュメントタイトル および キーワード
未加工テキストの取得 (および here および here および here (フォーカスの位置))
Searching (および here )(すべてのPDFで動作しません(奇妙な文字を表示するものもあります。エンコードの問題だと思いますが、確信はありません)-Credit BrainFeeder )
CALayer and Off-Screen Rendering -次のページをレンダリングして高速/スムーズに表示します
ドキュメント
サンプルプロジェクト
UIScrollView
、CATiledLayer
UIScrollView
、CATiledView
私は、ほぼ同じアプローチを使用して、このような種類のアプリケーションを構築しました。
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
オブジェクトは、取り除く方法を見つけられなかった内部キャッシュを実行しているようです。
シンプルで効果的な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];
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)
}