Visualizzatore PDF veloce e snello per iPhone / iPad / iO - suggerimenti e consigli?


369

Recentemente ci sono state molte domande sul disegno di PDF.

Sì, puoi rendere i PDF molto facilmente con un UIWebViewma questo non può dare le prestazioni e le funzionalità che ti aspetteresti da un buon visualizzatore di PDF.

È possibile disegnare una pagina PDF su un CALayer o su un UIImage . Apple ha persino un codice di esempio per mostrare come disegnare un PDF di grandi dimensioni in UIScrollview Zoomable

Ma gli stessi problemi continuano a emergere.

Metodo UIImage:

  1. I PDF sono in UIImagescala non otticamente e anche a livello.
  2. La CPU e la memoria colpiscono generando il UIImagesda un PDFcontext limite / impedisce di usarlo per creare un rendering in tempo reale di nuovi livelli di zoom.

Metodo CATiledLayer:

  1. C'è un notevole sovraccarico (tempo) che disegna una pagina PDF completa in un CALayer: è possibile visualizzare il rendering di singole tessere (anche con una modifica della piastrella)
  2. CALayers non può essere preparato in anticipo (reso fuori dallo schermo).

Generalmente anche i visualizzatori di PDF hanno una memoria piuttosto pesante. Monitora anche l'utilizzo della memoria dell'esempio PDF zoomabile di Apple.

Nel mio progetto attuale, sto sviluppando un visualizzatore di PDF e sto eseguendo il rendering di una UIImagedi una pagina in un thread separato (problemi anche qui!) E presentandolo mentre la scala è x1. CATiledLayeril rendering entra in funzione quando la scala è> 1. iBooks adotta un approccio simile a doppio approccio, come se si scorre le pagine è possibile vedere una versione a bassa risoluzione della pagina per appena meno di un secondo prima che appaia una versione nitida.

Sto mettendo a fuoco 2 pagine su ciascun lato della pagina in modo che l'immagine PDF sia pronta per mascherare il livello prima che inizi a disegnare. Le pagine vengono nuovamente distrutte quando si trovano a +2 pagine dalla pagina focalizzata.

Qualcuno ha qualche intuizione, non importa quanto piccolo o ovvio per migliorare le prestazioni / gestione della memoria di Drawing PDF? o altri problemi discussi qui?

EDIT: Alcuni suggerimenti (credito - Luke Mcneice, VdesmedT, Matt Gallagher, Johann):

  • Salvare qualsiasi supporto su disco quando è possibile.

  • Usa dimensioni delle piastrelle più grandi se esegui il rendering su TiledLayers

  • init usa frequentemente array con oggetti segnaposto, alternativamente un altro approccio progettuale è questo

  • Si noti che le immagini verranno visualizzate più velocemente di a CGPDFPageRef

  • Utilizzare NSOperationso GCD e blocchi per preparare le pagine in anticipo.

  • chiamare CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);prima CGContextDrawPDFPageper ridurre l'utilizzo della memoria durante il disegno

  • Iniziare il tuo NSOperationscon un docRef è una cattiva idea (memoria), avvolgere il docRef in un singleton.

  • Annulla inutile NSOperationsQuando puoi, specialmente se useranno la memoria, fai attenzione a lasciare aperti i contesti!

  • Ricicla gli oggetti della pagina e distruggi le viste inutilizzate

  • Chiudi tutti i contesti aperti non appena non ti servono

  • alla ricezione di avvisi di memoria rilasciare e ricaricare DocRef e le eventuali cache delle pagine

Altre caratteristiche PDF:

Documentazione

Progetti di esempio


commentare per assicurarsi che i capolino ricevano la notifica di modifica
Luke Mcneice il

+1 e grazie per aver aggiunto tutte queste informazioni, vorrei averlo mentre stavo sviluppando il mio lettore;) grazie anche per aver aggiunto la mia domanda sulle annotazioni PDF (contiene anche le risposte con codice di esempio). qualche giorno fa l'ho aperto: stackoverflow.com/questions/4097044/pdf-search-on-the-iphone hai qualche consiglio?
pt2ph8,

Non l'ho ancora trattato da solo, quindi non posso dire altro che indicarti il blog di idee casuali: random-ideas.net/posts/42 Grazie per il post, sto cercando di raccogliere tutti i problemi PDF in uno posto.
Luke Mcneice,

8
Nella mia azienda abbiamo usato per il rendering Pdf, notazione ecc. Una soluzione di terze parti chiamata PSPDFKit, non è economica, ma vale la pena: pspdfkit.com
János

1
+1 Ho seguito questi suggerimenti utili per il mio visualizzatore di pdf open source Swifty PDF github.com/prcela/SwiftyPDF
Prcela

Risposte:


103

Ho creato questo tipo di applicazione usando approssimativamente lo stesso approccio tranne:

  • Cache l'immagine generata sul disco e genera sempre due o tre immagini in anticipo in un thread separato.
  • Non sovrappongo a UIImagema disegna invece l'immagine nel livello quando lo zoom è 1. Quelle tessere verranno rilasciate automaticamente quando vengono emessi avvisi di memoria.

Ogni volta che l'utente inizia a eseguire lo zoom, acquisisco CGPDFPagee lo rendering utilizzando il marchio comunitario appropriato. Il codice in - (void)drawLayer: (CALayer*)layer inContext: (CGContextRef) contextè come:

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

problema è l'oggetto che contiene il file CGPDFDocumentRef. Sincronizzo la parte in cui pdfDocaccedo alla proprietà perché la rilascio e la ricrea quando ricevo memoryWarnings. Sembra che l' CGPDFDocumentRefoggetto esegua una memorizzazione nella cache interna di cui non ho trovato il modo di sbarazzarmi.


1
qual è il tuo approccio quando l'utente inizia a eseguire lo zoom?
Luke Mcneice,

2
@Luke: ho modificato il post per rispondere
VdesmedT

1
Grazie mille per questo, e il suggerimento per la memorizzazione nella cache CGPDFDocumentRef.
Luke Mcneice,

Solo una domanda veloce: perché stai ottenendo pdfPageRef e trasformalo quando la scala è 1.0? perché vedo che disegni una baseImage (l'immagine dall'operaio bg?) prima di creare la trasformazione.
Luke Mcneice,

@Luke: pubblica qui eventuali miglioramenti delle prestazioni che potresti apportare per favore. Grazie !
VdesmedT

67

Per un visualizzatore di PDF semplice ed efficace, quando si richiedono solo funzionalità limitate, è ora possibile (iOS 4.0+) utilizzare il framework QuickLook:

Per prima cosa, devi collegare contro QuickLook.frameworke#import <QuickLook/QuickLook.h>;

Successivamente, in uno viewDidLoado in uno dei metodi di inizializzazione lazy:

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

Sì, concettualmente è lo stesso che usare UIWebView. Non passeremmo ore a capire come usare roba CGPDF * se fosse così facile.
pt2ph8,

5
Si certo. Certamente non lo presento come una soluzione completa. Ma alcuni lettori di questa domanda potrebbero non essere consapevoli del fatto che, per alcuni scopi, questa è una soluzione ragionevole. Aggiornato la risposta per chiarirlo.
Joshua J. McKinnon,

6
+1 per questo. Il mio interesse principale è consentire all'utente di visualizzare alcuni documenti in-app in formato PDF. Non mi interessa la ricerca, l'evidenziazione o qualsiasi altra campana e fischio, solo un rendering PDF performante.
Memmons,

2
La mia categoria UIImage + PDF è un veloce renderer PDF senza fronzoli con un livello di cache incorporato. github.com/mindbrix/UIImage-PDF
Mindbrix

6

Da iOS 11 , puoi utilizzare il framework nativo chiamato PDFKit per visualizzare e manipolare i PDF.

Dopo aver importato PDFKit, è necessario inizializzare un PDFViewcon un URL locale o remoto e visualizzarlo nella vista.

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

Maggiori informazioni su PDFKit nella documentazione per gli sviluppatori Apple.


-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);
     } }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.