Come convertire UIView in PDF all'interno di iOS?


87

Ci sono molte risorse su come visualizzare un PDF in un'app UIView. Quello su cui sto lavorando ora è creare un PDF da UIViews.

Per esempio, ho una UIView, con subviews come Textviews, UILabels, UIImages, così come posso convertire un grande UIView nel suo insieme con tutti i suoi subviews e subsubviews ad un PDF?

Ho controllato il riferimento iOS di Apple . Tuttavia, parla solo di scrivere parti di testo / immagine in un file PDF.

Il problema che sto affrontando è che il contenuto che voglio scrivere su un file come PDF è molto. Se li scrivo nel PDF pezzo per pezzo, sarà un lavoro enorme da fare. Ecco perché sto cercando un modo per scrivere UIViewsin PDF o anche in bitmap.

Ho provato il codice sorgente che ho copiato da altre domande / risposte all'interno di Stack Overflow. Ma mi dà solo un PDF vuoto con la UIViewdimensione dei limiti.

-(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
    // Creates a mutable data object for updating with binary data, like a byte array
    NSMutableData *pdfData = [NSMutableData data];

    // Points the pdf converter to the mutable data object and to the UIView to be converted
    UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
    UIGraphicsBeginPDFPage();

    // draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
    [aView drawRect:aView.bounds];

    // remove PDF rendering context
    UIGraphicsEndPDFContext();

    // Retrieves the document directories from the iOS device
    NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);

    NSString* documentDirectory = [documentDirectories objectAtIndex:0];
    NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];

    // instructs the mutable data object to write its context to a file on disk
    [pdfData writeToFile:documentDirectoryFilename atomically:YES];
    NSLog(@"documentDirectoryFileName: %@",documentDirectoryFilename);
}

Risposte:


124

Notare che il seguente metodo crea solo una bitmap della vista; non crea la tipografia reale.

(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
    // Creates a mutable data object for updating with binary data, like a byte array
    NSMutableData *pdfData = [NSMutableData data];

    // Points the pdf converter to the mutable data object and to the UIView to be converted
    UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
    UIGraphicsBeginPDFPage();
    CGContextRef pdfContext = UIGraphicsGetCurrentContext();


    // draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData

    [aView.layer renderInContext:pdfContext];

    // remove PDF rendering context
    UIGraphicsEndPDFContext();

    // Retrieves the document directories from the iOS device
    NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);

    NSString* documentDirectory = [documentDirectories objectAtIndex:0];
    NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];

    // instructs the mutable data object to write its context to a file on disk
    [pdfData writeToFile:documentDirectoryFilename atomically:YES];
    NSLog(@"documentDirectoryFileName: %@",documentDirectoryFilename);
}

Assicurati anche di importare: QuartzCore / QuartzCore.h


2
+1 Ho letto diversi post sulla generazione di pdf prima di trovare questa semplice soluzione.
Jason George

7
Volevo fare lo stesso e il tuo metodo sembra funzionare bene ma la sua qualità è molto bassa. Mi sono perso qualcosa?
iEamin

7
Sospetto che la qualità sia piuttosto bassa perché sta prendendo l'UIView e lo converte in un raster, dove come gli altri metodi di rendering di testo e immagini li conserva direttamente come vettori nel file PDF.
joshaidan

3
Sto seguendo questo metodo, ma ricevo un pdf vuoto generato. Qualcuno può aiutarmi ?
Raj

Funziona alla grande !!! Saluti !! l'unico problema che ho è che genera PDF in una sola pagina. Come posso separare le pagine invece di avere un lungo file PDF?!
Rudi

25

Inoltre, se qualcuno è interessato, ecco il codice Swift 3:

func createPdfFromView(aView: UIView, saveToDocumentsWithFileName fileName: String)
{
    let pdfData = NSMutableData()
    UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil)
    UIGraphicsBeginPDFPage()

    guard let pdfContext = UIGraphicsGetCurrentContext() else { return }

    aView.layer.render(in: pdfContext)
    UIGraphicsEndPDFContext()

    if let documentDirectories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
        let documentsFileName = documentDirectories + "/" + fileName
        debugPrint(documentsFileName)
        pdfData.write(toFile: documentsFileName, atomically: true)
    }
}

1
questo crea PDF solo per firstPage! che dire di scrollview?
Saurabh Prajapati

Ottima domanda! Tuttavia, non sono io a chiedere. Forse iniziare un'altra domanda?
retrovius

Ho lo stesso problema quindi @SaurabhPrajapati e ho creato una domanda
Jonas Deichelmann

10

Se qualcuno è interessato, ecco il codice Swift 2.1:

    func createPdfFromView(aView: UIView, saveToDocumentsWithFileName fileName: String)
    {
        let pdfData = NSMutableData()
        UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil)
        UIGraphicsBeginPDFPage()

        guard let pdfContext = UIGraphicsGetCurrentContext() else { return }

        aView.layer.renderInContext(pdfContext)
        UIGraphicsEndPDFContext()

        if let documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first {
            let documentsFileName = documentDirectories + "/" + fileName
            debugPrint(documentsFileName)
            pdfData.writeToFile(documentsFileName, atomically: true)
        }
    }

La tua dichiarazione di guardia significa che UIGraphicsEndPDFContext () non è chiamato - forse aggiungi un differimento prima?
David H

@DavidH grazie, David, buona idea! Inoltre, penso che ci sia una buona idea aggiungere un tipo di blocco di completamento completion: (success: Bool) -> ()per i casi di restituzione della guardia
Denis Rumiantsev

1
Ieri ho pubblicato una domanda e risposta su come produrre un'immagine ad alta risoluzione rendendo la vista in un'immagine grande, quindi disegnando l'immagine in un PDF interessato: stackoverflow.com/a/35442187/1633251
David H

5

Un modo semplicissimo per creare un PDF da UIView è usare l'estensione UIView

Swift 4.2

extension UIView {

  // Export pdf from Save pdf in drectory and return pdf file path
  func exportAsPdfFromView() -> String {

      let pdfPageFrame = self.bounds
      let pdfData = NSMutableData()
      UIGraphicsBeginPDFContextToData(pdfData, pdfPageFrame, nil)
      UIGraphicsBeginPDFPageWithInfo(pdfPageFrame, nil)
      guard let pdfContext = UIGraphicsGetCurrentContext() else { return "" }
      self.layer.render(in: pdfContext)
      UIGraphicsEndPDFContext()
      return self.saveViewPdf(data: pdfData)

  }

  // Save pdf file in document directory
  func saveViewPdf(data: NSMutableData) -> String {  
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let docDirectoryPath = paths[0]
    let pdfPath = docDirectoryPath.appendingPathComponent("viewPdf.pdf")
    if data.write(to: pdfPath, atomically: true) {
        return pdfPath.path
    } else {
        return ""
    }
  }
}

Credito: http://www.swiftdevcenter.com/create-pdf-from-uiview-wkwebview-and-uitableview/


Grazie ha funzionato, una domanda, quindi ho una visualizzazione a scorrimento lungo, ma il file PDF ne mostra solo una parte, quindi c'è un modo per modificare il codice, ad esempio per dargli Altezza?
Hussein Elbeheiry

@HusseinElbeheiry usa solo contentView per generare pdf. Quando creo uno scrollView (UIScrollView) creerò sicuramente un contentView (UIView) e inserirò il contentView nello scrollView e aggiungerò tutti gli elementi successivi al contentView. In questo caso, è sufficiente utilizzare contentView per creare un documento PDF. contentView.exportAsPdfFromView
iAleksandr

3

Con Swift 5 / iOS 12, puoi combinare CALayeril render(in:)metodo di con UIGraphicsPDFRendereril writePDF(to:withActions:)metodo di per creare un file PDF da UIViewun'istanza.


Il seguente codice di esempio Playground mostra come usare render(in:)e writePDF(to:withActions:):

import UIKit
import PlaygroundSupport

let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
view.backgroundColor = .orange
let subView = UIView(frame: CGRect(x: 20, y: 20, width: 40, height: 60))
subView.backgroundColor = .magenta
view.addSubview(subView)

let outputFileURL = PlaygroundSupport.playgroundSharedDataDirectory.appendingPathComponent("MyPDF.pdf")
let pdfRenderer = UIGraphicsPDFRenderer(bounds: view.bounds)

do {
    try pdfRenderer.writePDF(to: outputFileURL, withActions: { context in
        context.beginPage()
        view.layer.render(in: context.cgContext)
    })
} catch {
    print("Could not create PDF file: \(error)")
}

Nota: per poterlo utilizzare playgroundSharedDataDirectorynel tuo parco giochi, devi prima creare una cartella chiamata "Dati del parco giochi condivisi" nella cartella "Documenti" di macOS.


L' UIViewControllerimplementazione completa della sottoclasse di seguito mostra un possibile modo per eseguire il refactoring dell'esempio precedente per un'app iOS:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        view.backgroundColor = .orange
        let subView = UIView(frame: CGRect(x: 20, y: 20, width: 40, height: 60))
        subView.backgroundColor = .magenta
        view.addSubview(subView)

        createPDF(from: view)
    }

    func createPDF(from view: UIView) {
        let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let outputFileURL = documentDirectory.appendingPathComponent("MyPDF.pdf")
        print("URL:", outputFileURL) // When running on simulator, use the given path to retrieve the PDF file

        let pdfRenderer = UIGraphicsPDFRenderer(bounds: view.bounds)

        do {
            try pdfRenderer.writePDF(to: outputFileURL, withActions: { context in
                context.beginPage()
                view.layer.render(in: context.cgContext)
            })
        } catch {
            print("Could not create PDF file: \(error)")
        }
    }

}

2

Questo genererà PDF da UIView e aprirà la finestra di dialogo di stampa, obiettivo C. Allega - (IBAction)PrintPDF:(id)senderal tuo pulsante sullo schermo. Aggiungi #import <QuartzCore/QuartzCore.h>framework

File H.

    @interface YourViewController : UIViewController <MFMailComposeViewControllerDelegate,UIPrintInteractionControllerDelegate>

    {
    UIPrintInteractionController *printController;
    }

- (IBAction)PrintPDF:(id)sender;

File M.

-(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename

{
    NSMutableData *pdfData = [NSMutableData data];

    UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
    UIGraphicsBeginPDFPage();
    CGContextRef pdfContext = UIGraphicsGetCurrentContext();


    [aView.layer renderInContext:pdfContext];
    UIGraphicsEndPDFContext();

    NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);

    NSString* documentDirectory = [documentDirectories objectAtIndex:0];
    NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
    NSString *file = [documentDirectory stringByAppendingPathComponent:@"yourPDF.pdf"];
    NSURL *urlPdf = [NSURL fileURLWithPath: file];

    [pdfData writeToFile:documentDirectoryFilename atomically:YES];
    NSLog(@"documentDirectoryFileName: %@",documentDirectoryFilename);

}


- (IBAction)PrintPDF:(id)sender
{
    [self createPDFfromUIView:self.view saveToDocumentsWithFileName:@"yourPDF.pdf"];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"yourPDF.pdf"];
    NSData *myData = [NSData dataWithContentsOfFile: path];

    UIPrintInteractionController *pic = [UIPrintInteractionController sharedPrintController];
    if(pic && [UIPrintInteractionController canPrintData: myData] ) {

        pic.delegate = self;

        UIPrintInfo *printInfo = [UIPrintInfo printInfo];
        printInfo.outputType = UIPrintInfoOutputGeneral;
        printInfo.jobName = [path lastPathComponent];
        printInfo.duplex = UIPrintInfoDuplexLongEdge;
        pic.printInfo = printInfo;
        pic.showsPageRange = YES;
        pic.printingItem = myData;

        void (^completionHandler)(UIPrintInteractionController *, BOOL, NSError *) = ^(UIPrintInteractionController *pic, BOOL completed, NSError *error) {
            //self.content = nil;
            if(!completed && error){

                NSLog(@"Print Error: %@", error);
            }
        };

        [pic presentAnimated:YES completionHandler:completionHandler];

    }

}

-4

Non so perché, ma la risposta di Casilic mi dà una schermata vuota su iOS6.1. Il codice seguente funziona.

-(NSMutableData *)createPDFDatafromUIView:(UIView*)aView 
{
    // Creates a mutable data object for updating with binary data, like a byte array
    NSMutableData *pdfData = [NSMutableData data];

    // Points the pdf converter to the mutable data object and to the UIView to be converted
    UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
    UIGraphicsBeginPDFPage();
    CGContextRef pdfContext = UIGraphicsGetCurrentContext();


    // draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData

    [aView.layer renderInContext:pdfContext];

    // remove PDF rendering context
    UIGraphicsEndPDFContext();

    return pdfData;
}


-(NSString*)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
    // Creates a mutable data object for updating with binary data, like a byte array
    NSMutableData *pdfData = [self createPDFDatafromUIView:aView];

    // Retrieves the document directories from the iOS device
    NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);

    NSString* documentDirectory = [documentDirectories objectAtIndex:0];
    NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];

    // instructs the mutable data object to write its context to a file on disk
    [pdfData writeToFile:documentDirectoryFilename atomically:YES];
    NSLog(@"documentDirectoryFileName: %@",documentDirectoryFilename);
    return documentDirectoryFilename;
}

16
Questo è lo stesso codice esatto della mia risposta appena suddivisa in due metodi separati ???? Come pensi che questo abbia risolto il problema dello schermo vuoto quando è lo stesso codice ??
casillic

Ho avuto la stessa esperienza. Ho un PDF vuoto dal primo codice. Dividendolo in due come ha fatto Alex, ha funzionato. Non so spiegare perché.
Tom Tallak Solbu
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.