Il y a eu récemment beaucoup de questions sur le dessin de PDF.
Oui, vous pouvez très facilement rendre les PDF avec un UIWebView
, mais cela ne donne pas les performances et les fonctionnalités que vous attendez d'un bon PDF spectateur.
Vous pouvez dessiner une PDF page vers un CALayer ou vers un UIImage . Apple ont même un exemple de code montrant comment dessiner un grand PDF dans une UIScrollview zoomable
Mais les mêmes problèmes ne cessent de surgir.
Méthode UIImage:
UIImage
n'échelonnent pas optiquement aussi bien qu'une approche par couche.UIImages
à partir d'une PDFcontext
limite/empêche de l'utiliser pour créer un rendu en temps réel de nouveaux niveaux de zoom.Méthode CATiledLayer:
CALayer
: on peut voir le rendu des tuiles individuelles (même avec un tileSize Tweak)CALayers
ne peut pas être préparé à l'avance (rendu hors écran).En règle générale, PDF les téléspectateurs ont également beaucoup de mémoire. Surveillez même l'utilisation de la mémoire de l'exemple PDF zoomable d'Apple.
Dans mon projet actuel, je développe une visionneuse PDF et je suis en train de rendre une UIImage
d'une page dans un fil séparé (dans ce cas aussi!) Et de la présenter pendant que l'échelle est x1. CATiledLayer
le rendu commence lorsque la balance est> 1. iBooks adopte une approche similaire en matière de double prise, comme si vous faites défiler les pages, vous pouvez voir une version de résolution inférieure de la page un peu moins d'une seconde avant qu'une version nette ne s'affiche.
Le rendu est effectué sur 2 pages de chaque côté de la page afin que l'image PDF soit prête à masquer le calque avant de commencer à dessiner. Les pages sont à nouveau détruites à +2 pages de la page sélectionnée.
Quelqu'un a-t-il des idées, aussi minimes soit-elles, pour améliorer la gestion des performances/de la mémoire des PDF Drawing? ou d'autres questions discutées ici?
EDIT: Quelques astuces (Crédit: Luke Mcneice, VdesmedT, Matt Gallagher, Johann):
Enregistrez tout support sur le disque quand vous le pouvez.
Utiliser des tailles de pavé plus grandes si le rendu est effectué sur des couches de mosaïque
arrays fréquemment utilisés avec des objets de substitution, une autre approche de conception est: celle-ci
Notez que les images seront rendues plus rapidement qu'un CGPDFPageRef
Utilisez NSOperations
ou GCD & Blocks pour préparer les pages à l’avance.
appelez CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);
avant CGContextDrawPDFPage
pour réduire l'utilisation de la mémoire pendant le dessin
initier votre NSOperations
avec un docRef est une mauvaise idée (mémoire), enveloppez le docRef dans un singleton.
Annuler inutile NSOperations
Lorsque vous pouvez, surtout s’ils utiliseront la mémoire, méfiez-vous de laisser les contextes ouverts!
Recycler les objets de page et détruire les vues inutilisées
Fermez tous les contextes ouverts dès que vous n'en avez plus besoin
lors de la réception des avertissements de mémoire, relâchez et rechargez la DocRef et toutes les pages Caches
Autres PDF Caractéristiques:
Obtenir des liens dans un PDF (et ici et ici )
Obtention de la cible du lien (Obtention du numéro de page du tableau /Dest
)
Obtenir du texte brut (et ici et ici et ici (positionnement centré))
Recherche (et ici ) (ne fonctionne pas avec tous les PDF (certains affichent simplement des caractères étranges, je suppose que c'est un problème d'encodage mais je ne suis pas sûr) -Credit BrainFeeder )
CALayer et Off-Screen Rendering - Rend la page suivante pour un affichage rapide/fluide
Documentation
Exemples de projets
UIScrollView
, CATiledLayer
UIScrollView
, CATiledView
J'ai construit ce genre d'application en utilisant approximativement la même approche sauf:
UIImage
, mais dessine l'image dans le calque lorsque le zoom est égal à 1. Ces tuiles sont automatiquement libérées lorsque des avertissements de mémoire sont émis.À chaque fois que l'utilisateur commence à zoomer, j'obtiens la CGPDFPage
et la restitue à l'aide du CTM approprié. Le code dans - (void)drawLayer: (CALayer*)layer inContext: (CGContextRef) context
est comme:
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 est l'objet contenant la CGPDFDocumentRef
. Je synchronise la partie où j'accède à la propriété pdfDoc
parce que je la libère et la recrée lors de la réception de memoryWarnings. Il semble que l’objet CGPDFDocumentRef
effectue une mise en cache interne dont je n’ai pas trouvé le moyen de se débarrasser.
Pour une visionneuse PDF simple et efficace, lorsque vous n’avez besoin que de fonctionnalités limitées, vous pouvez désormais (iOS 4.0+) utiliser le cadre QuickLook:
Tout d’abord, vous devez créer un lien avec QuickLook.framework
et #import <QuickLook/QuickLook.h>;
Ensuite, dans viewDidLoad
ou l’une des méthodes d’initialisation différée:
QLPreviewController *previewController = [[QLPreviewController alloc] init];
previewController.dataSource = self;
previewController.delegate = self;
previewController.currentPreviewItemIndex = indexPath.row;
[self presentModalViewController:previewController animated:YES];
[previewController release];
Depuis iOS 11 , vous pouvez utiliser le framework natif appelé PDFKit pour afficher et manipuler des PDF.
Après avoir importé PDFKit, vous devez initialiser un PDFView
avec une URL locale ou distante et l’afficher dans votre affichage.
if let url = Bundle.main.url(forResource: "example", withExtension: "pdf") {
let pdfView = PDFView(frame: view.frame)
pdfView.document = PDFDocument(url: url)
view.addSubview(pdfView)
}
Pour en savoir plus sur PDFKit dans la Apple Documentation du développeur.