lanzamiento - ios 12 iphone 7
Visor de PDF rápido y ligero para iPhone/iPad/iOs: ¿consejos y sugerencias? (4)
Desde iOS 11 , puede usar el marco nativo llamado PDFKit para visualizar y manipular archivos PDF.
Después de importar PDFKit, debe inicializar un PDFView
con una URL local o remota y mostrarlo 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 para desarrolladores de Apple.
Ha habido muchas preguntas recientemente sobre el dibujo de PDF.
Sí, puede renderizar PDF''s muy fácilmente con un UIWebView
pero esto no puede proporcionar el rendimiento y la funcionalidad que esperaría de un buen visor de PDF.
Puede dibujar una página PDF para un CALayer o un UIImage . Apple incluso tiene código de ejemplo para mostrar cómo dibujar un PDF grande en una vista de UIScroll con Zoomable
Pero los mismos problemas siguen apareciendo.
Método UIImage:
- Los PDF en un
UIImage
no se escalan ópticamente, así como un enfoque de Capa. - La CPU y la memoria
UIImages
la generación deUIImages
partir de unPDFcontext
limita / evita su uso para crear una representación en tiempo real de nuevos niveles de zoom.
Método CATiledLayer:
- Existe una sobrecarga significativa (tiempo) al dibujar una página PDF completa para un
CALayer
: se pueden ver mosaicos individuales (incluso con un ajuste deCALayer
azulejo) -
CALayers
no pueden prepararse con anticipación (renderizados fuera de pantalla).
En general, los visores de PDF también son bastante pesados en la memoria Incluso controlar el uso de 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 (¡problemas también aquí!) Y lo presento 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 se desplaza por las páginas, puede ver una versión con menor resolución de la página por solo menos de un segundo antes de que aparezca una versión nítida.
Estoy renderizando 2 páginas de cada lado de la página enfocada para que la imagen PDF esté lista para enmascarar la capa antes de comenzar a dibujar. Las páginas se destruyen de nuevo cuando están a una distancia de +2 de la página enfocada.
¿Alguien tiene alguna información, no importa cuán pequeña u obvia sea para mejorar el rendimiento / manejo de memoria de los PDF de Drawing? ¿O cualquier otro tema discutido aquí?
EDITAR: Algunos consejos (crédito- Luke Mcneice, VdesmedT, Matt Gallagher, Johann):
Guarda cualquier medio en el disco cuando puedas.
Utilice tamaños de azulejos más grandes si se renderiza en TiledLayers
A menudo se utilizan matrices con objetos de marcadores de posición, alternativamente, este es otro enfoque de diseño.
Tenga en cuenta que las imágenes se renderizarán más rápido que un
CGPDFPageRef
Use
NSOperations
o GCD & Blocks para preparar páginas con anticipación.llame a
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);
antes deCGContextDrawPDFPage
para reducir el uso de memoria mientras se dibujainiciar sus
NSOperations
con un docRef es una mala idea (memoria), envuelva el docRef en un singleton.Cancele las innecesarias
NSOperations
Cuando pueda, especialmente si van a usar la memoria, ¡tenga cuidado de dejar los contextos abiertos!Recicla objetos de la página y destruye vistas no utilizadas
Cierre cualquier contexto abierto tan pronto como no los necesite
Al recibir advertencias de memoria, libere y vuelva a cargar DocRef y cualquier página.
Otras características de PDF:
Obtención de enlaces dentro de un PDF (y here y here )
Obtención del destino del enlace (Obtención del número de página de la matriz
/Dest
)
Obtención de texto sin formato (y here y Here y here (posicionamiento centrado))
Searching (y here ) (no funciona con todos los archivos PDF (algunos solo muestran caracteres extraños, supongo que es un problema de codificación pero no estoy seguro) -Creditar BrainFeeder)
CALayer y reproducción fuera de pantalla : muestra la página siguiente para una visualización rápida y suave
Documentación
- PDFObjects de cuarzo (se utiliza para meta información, anotaciones, pulgares)
- Abobe PDF Spec
Proyectos de ejemplo
- Apple / ZoomingPDF - zoom,
UIScrollView
,CATiledLayer
- vfr / reader - zoom, paginación,
UIScrollView
,CATiledView
- frente / hojas - paginando con buenas transiciones
- / skim - todo lo que parece (lector / editor de PDF para OSX)
He desarrollado este tipo de aplicación utilizando aproximadamente el mismo enfoque, excepto:
- Almaceno en caché la imagen generada en el disco y siempre genero de dos a 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 mosaicos se liberarán automáticamente cuando se emitan advertencias de memoria.
Cada vez que el usuario inicia el zoom, CGPDFPage
el CGPDFPage
y lo represento utilizando 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);
}
}
problema es el objeto que contiene el CGPDFDocumentRef
. Sincronizo la parte donde pdfDoc
propiedad pdfDoc
porque la libero y la vuelvo a crear cuando recibo MemoryWarnings. Parece que el objeto CGPDFDocumentRef
un almacenamiento interno en caché del que no encontré cómo deshacerme de él.
Para un visor de PDF simple y efectivo, cuando solo necesita una funcionalidad limitada, ahora puede (iOS 4.0+) usar el marco QuickLook:
Primero, debe vincular con QuickLook.framework
y #import <QuickLook/QuickLook.h>;
Luego, en viewDidLoad
o en cualquiera de los métodos de inicialización perezosa:
QLPreviewController *previewController = [[QLPreviewController alloc] init];
previewController.dataSource = self;
previewController.delegate = self;
previewController.currentPreviewItemIndex = indexPath.row;
[self presentModalViewController:previewController animated:YES];
[previewController release];
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);
} }