Visor de PDF rápido y fácil para iPhone / iPad / iOs – consejos y sugerencias?

Recientemente ha habido muchas preguntas sobre el dibujo de PDF.

Sí, puedes renderizar PDFs muy fácilmente con un UIWebView pero esto no puede ofrecer el rendimiento y la funcionalidad que esperarías de un buen visor de PDF.

Puede dibujar una página PDF en un CALayer o en un UIImage . Apple incluso tiene un código de muestra para mostrar cómo dibujar un PDF grande en una vista UIScrollview de Zoomable

Pero los mismos problemas siguen apareciendo.

Método UIImage:

  1. Los PDF en un UIImage no se escalan ópticamente, así como un enfoque de Capa.
  2. La CPU y la memoria UIImages la generación de UIImages desde un PDFcontext limits / previene su uso para crear un render en tiempo real de nuevos niveles de zoom.

Método CATiledLayer:

  1. Hay una sobrecarga significativa (tiempo) dibujando una página PDF completa a un CALayer : se pueden ver las fichas individuales (incluso con un ajuste tileSize)
  2. CALayers no pueden prepararse antes de tiempo (se muestran fuera de la pantalla).

En general, los lectores de PDF también pesan demasiado en la memoria. Incluso monitoree el uso de la memoria del ejemplo de PDF con zoom de Apple.

En mi proyecto actual, estoy desarrollando un visor de PDF y estoy renderizando un UIImage de una página en un hilo separado (¡aquí también los problemas!) Y presentándolo mientras la escala es x1. CATiledLayer representación de CATiledLayer una vez que la escala es> 1. iBooks adopta un enfoque similar de doble toma, ya que si recorre las páginas puede ver una versión de resolución inferior de la página por menos de un segundo antes de que aparezca una versión nítida.

Estoy renderizando 2 páginas a cada lado de la página en foco para que la imagen PDF esté lista para enmascarar la capa antes de que comience el dibujo. Las páginas se destruyen nuevamente cuando están a más de 2 páginas de la página enfocada.

¿Alguien tiene alguna idea, no importa cuán pequeña u obvia sea para mejorar el rendimiento / manejo de memoria de Drawing PDF? o cualquier otro problema discutido aquí?

EDITAR: Algunos consejos (crédito: Luke Mcneice, VdesmedT, Matt Gallagher, Johann):

  • Guarde cualquier medio en el disco cuando pueda.

  • Usa tileSize más grandes si renderizas en TiledLayers

  • init arrays de uso frecuente con objetos de marcador de posición, alternativamente otro enfoque de diseño es este

  • Tenga en cuenta que las imágenes se procesarán más rápido que un CGPDFPageRef

  • Use NSOperations o GCD & Blocks para preparar las páginas con anticipación.

  • llamar a CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault); CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault); antes de CGContextDrawPDFPage para reducir el uso de memoria mientras se dibuja

  • iniciando sus NSOperations con un docRef es una mala idea (memoria), envuelva docRef en un singleton.

  • Cancele las innecesarias NSOperations Cuando pueda, especialmente si van a usar memoria, ¡tenga cuidado de no dejar contextos abiertos!

  • Recicle los objetos de la página y destruya las vistas no utilizadas

  • Cierre cualquier contexto abierto tan pronto como no los necesite

  • al recibir advertencias de memoria, suelte y vuelva a cargar DocRef y cualquier página Cachés

Otras características de PDF:

  • Obtener enlaces dentro de un PDF (y aquí y aquí )

    • Comprender el PDF Rect para el posicionamiento del enlace

    • Conversión de cadenas de fechas de PDF anot

    • Obteniendo el objective del enlace (Obteniendo el número de página de la matriz /Dest )

  • Obtener una tabla de contenido

  • Título del documento y palabras clave

  • Obteniendo texto en bruto (y aquí y aquí y aquí (posicionamiento centrado))

  • Buscando (y aquí ) (no funciona con todos los archivos PDF (algunos solo muestran caracteres extraños, supongo que es un problema de encoding, pero no estoy seguro) -Credit BrainFeeder)

  • CALayer y Off-Screen Rendering : muestra la página siguiente para una visualización rápida / fluida

Documentación

  • Quartz PDFObjects (Usado para metainformación, anotaciones, pulgares)
  • Abobe PDF Spec

Proyectos de ejemplo

  • Apple / ZoomingPDF – zoom, UIScrollView , CATiledLayer
  • vfr / reader – zooming , paginación, UIScrollView , CATiledView
  • frente / hojas – paginación con buenas transiciones
  • / skim – todo lo que parece (lector / editor de PDF para OSX)

He desarrollado ese tipo de aplicación utilizando aproximadamente el mismo enfoque, excepto:

  • Guardo en la memoria caché la imagen generada en el disco y siempre genero dos o tres imágenes por adelantado en un hilo separado.
  • No superpongo con un UIImage sino que dibujo la imagen en la capa cuando el zoom es 1. Esos azulejos se liberarán automáticamente cuando se emitan advertencias de memoria.

Cada vez que el usuario comienza a hacer zoom, CGPDFPage y lo renderizo usando el CTM apropiado. El código en - (void)drawLayer: (CALayer*)layer inContext: (CGContextRef) context es como:

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

El problema es el objeto que contiene el CGPDFDocumentRef . Sincronizo la parte donde pdfDoc propiedad pdfDoc porque la pdfDoc la pdfDoc crear al recibir memoriaAdministración. Parece que el objeto CGPDFDocumentRef hace un poco de almacenamiento en caché interno que no encontré cómo deshacerme de él.

Para un visor de PDF simple y eficaz, cuando requiera solo una funcionalidad limitada, puede ahora (iOS 4.0+) usar el marco QuickLook:

En primer lugar, debe establecer un vínculo con QuickLook.framework y #import ;

Después, en viewDidLoad o cualquiera de los métodos de inicialización viewDidLoad :

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

Desde iOS 11 , puede utilizar el marco nativo denominado PDFKit para visualizar y manipular archivos PDF.

Después de importar PDFKit, debe inicializar una PDFView con una URL local o remota y mostrarla en su 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) } 

Lea más sobre PDFKit en la documentación del desarrollador de Apple.

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