web-dev-qa-db-pt.com

Rápido e Lean PDF Viewer para iPhone / iPad / iOs - dicas e sugestões?

Recentemente, houve muitas perguntas sobre o desenho de PDFs.

Sim, você pode renderizar PDF facilmente com um UIWebView, mas isso não dá o desempenho e a funcionalidade que você esperaria de um bom visualizador PDF.

Você pode desenhar uma página PDF para um CALayer ou para um UIImage . Apple ainda tem código de exemplo para mostrar como desenhar um grande PDF em um UIScrollview do Zoomable

Mas os mesmos problemas continuam surgindo.

Método UIImage:

  1. Os PDFs em um UIImage não são escalados opticamente, assim como a abordagem Layer.
  2. A CPU e a memória atingidas ao gerar o UIImages a partir de um limite PDFcontext/evitam usá-lo para criar uma renderização em tempo real de novos níveis de zoom.

Método CATiledLayer:

  1. Há uma sobrecarga significativa (tempo) desenhando uma página inteira PDF para um CALayer: blocos individuais podem ser vistos renderizados (mesmo com um tileSize Tweak)
  2. CALayers não pode ser preparado antes do tempo (renderizado fora da tela).

Geralmente, os visualizadores PDF também são muito pesados ​​na memória. Até mesmo monitore o uso de memória do exemplo de zoom da Apple PDF.

No meu projeto atual, estou desenvolvendo um visualizador PDF e estou processando um UIImage de uma página em um thread separado (problemas aqui também!) E apresentando-o enquanto a escala é x1. O processamento de CATiledLayer entra em ação assim que a escala é> 1. O iBooks adota uma abordagem dupla semelhante, como se você rolasse as páginas e pudesse ver uma versão de baixa resolução da página por menos de um segundo antes de uma versão nítida aparecer.

Im renderizando 2 páginas em cada lado da página em foco para que a imagem PDF esteja pronta para mascarar a camada antes de começar a desenhar. As páginas são destruídas novamente quando estão a +2 páginas da página em foco.

Alguém tem algum insight, não importa quão pequeno ou óbvio para melhorar o desempenho/manuseio de memória do Drawing PDF's? ou quaisquer outras questões discutidas aqui?

EDIT: Algumas Dicas (Crédito: Luke Mcneice, VdesmedT, Matt Gallagher, Johann):

  • Salve qualquer mídia no disco quando puder.

  • Use tilesSizes maiores se renderizando em TiledLayers

  • iniciar matrizes freqüentemente usadas com objetos de espaço reservado, alternativamente outra abordagem de design é este

  • Observe que as imagens serão renderizadas mais rapidamente que um CGPDFPageRef

  • Use NSOperations ou GCD & Blocks para preparar as páginas antes do tempo.

  • chame CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault); antes de CGContextDrawPDFPage para reduzir o uso de memória ao desenhar

  • iniciar seu NSOperations com um docRef é uma má idéia (memória), coloque o docRef em um singleton.

  • Cancelar desnecessário NSOperations Quando você puder, especialmente se eles estiverem usando memória, tenha cuidado para deixar contextos abertos!

  • Reciclar objetos de página e destruir vistas não utilizadas

  • Feche todos os contextos abertos assim que você não precisar deles

  • ao receber avisos de memória, libere e recarregue o DocRef e qualquer página Caches

Outros recursos PDF:

Documentação

Projetos de exemplo

365
Luke Mcneice

Eu construí esse tipo de aplicativo usando aproximadamente a mesma abordagem, exceto:

  • Eu armazeno em cache a imagem gerada no disco e sempre gero duas a três imagens com antecedência em um thread separado.
  • Eu não sobreponho com um UIImage mas em vez disso, desenho a imagem na camada quando o zoom é 1. Esses blocos serão liberados automaticamente quando os avisos de memória forem emitidos.

Sempre que o usuário começar a aplicar zoom, eu adquiro o CGPDFPage e o renderizo usando o CTM apropriado. O código em - (void)drawLayer: (CALayer*)layer inContext: (CGContextRef) context é 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);
    }
}

issue é o objeto que contém o CGPDFDocumentRef. Eu sincronizo a parte onde eu acesso a propriedade pdfDoc porque eu libero-o e recrio-o ao receber memoryWarnings. Parece que o objeto CGPDFDocumentRef faz algum cache interno que eu não encontrei como me livrar.

103
VdesmedT

Para um visualizador PDFsimples e eficaz, quando você precisa de apenas uma funcionalidade limitada, agora você pode (iOS 4.0+) usar a estrutura do QuickLook:

Primeiro, você precisa vincular QuickLook.framework e #import <QuickLook/QuickLook.h>;

Depois, em viewDidLoad ou em qualquer um dos métodos de inicialização lenta:

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

Desde iOS 11 , você pode usar o framework nativo chamado PDFKit para exibir e manipulando PDFs.

Depois de importar o PDFKit, você deve inicializar um PDFView com um URL local ou remoto e exibi-lo na sua visualização.

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

Leia mais sobre o PDFKit na Apple documentação do desenvolvedor.

6
Tamás Sengel