Wie konvertiere ich UIView in PDF unter iOS?

87

Es gibt viele Ressourcen zum Anzeigen einer PDF-Datei in einer App UIView. Ich arbeite gerade daran, ein PDF aus zu erstellen UIViews.

Zum Beispiel habe ich ein UIView, mit Subviews wie Textviews, UILabels, UIImages, so wie kann ich eine konvertieren groß UIView als Ganzes , einschließlich aller seiner Subviews und subsubviews zu einer PDF - Datei?

Ich habe Apples iOS-Referenz überprüft . Es geht jedoch nur darum, Text- / Bildteile in eine PDF-Datei zu schreiben.

Das Problem, mit dem ich konfrontiert bin, ist, dass der Inhalt, den ich als PDF in eine Datei schreiben möchte, sehr groß ist. Wenn ich sie Stück für Stück in das PDF schreibe, wird das eine enorme Arbeit. Deshalb suche ich nach einer Möglichkeit, UIViewsin PDFs oder sogar Bitmaps zu schreiben .

Ich habe den Quellcode ausprobiert, den ich aus anderen Fragen / Antworten in Stack Overflow kopiert habe. Aber es gibt mir nur ein leeres PDF mit der UIViewGrenzgröße.

-(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);
}
Cullen SUN
quelle

Antworten:

123

Beachten Sie, dass die folgende Methode nur eine Bitmap der Ansicht erstellt. Es wird keine tatsächliche Typografie erstellt.

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

Stellen Sie außerdem sicher, dass Sie Folgendes importieren: QuartzCore / QuartzCore.h

Casillic
quelle
2
+1 Ich habe mehrere Beiträge zur PDF-Generierung durchgesehen, bevor ich diese einfache Lösung gefunden habe.
Jason George
7
Ich wollte das Gleiche tun und Ihre Methode scheint gut zu funktionieren, aber ihre Qualität ist sehr gering. Habe ich etwas vergessen?
iEamin
7
Ich vermute, dass die Qualität ziemlich niedrig ist, da das UIView in ein Raster konvertiert wird, wobei die anderen Methoden zum Rendern von Text und Bildern diese direkt als Vektoren in der PDF-Datei beibehalten.
Joshaidan
3
Ich folge dieser Methode, aber ich bekomme ein leeres PDF generiert. Kann mir jemand helfen ?
Raj
Es funktioniert super !!! Prost !! Das einzige Problem, das ich habe, ist, dass es PDF auf nur einer Seite generiert. Wie kann ich Seiten trennen, anstatt eine lange PDF-Datei zu haben ?!
Rudi
24

Wenn jemand interessiert ist, ist hier außerdem der Swift 3-Code:

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)
    }
}
Retrovius
quelle
1
Dieses PDF wird nur für die erste Seite erstellt! Was ist mit Scrollview?
Saurabh Prajapati
Gute Frage! Ich bin jedoch nicht derjenige, der fragt. Vielleicht eine andere Frage anfangen?
Retrovius
Ich habe das gleiche Problem wie @SaurabhPrajapati und ich habe eine Frage erstellt
Jonas Deichelmann
10

Wenn jemand interessiert ist, hier ist Swift 2.1 Code:

    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)
        }
    }
Denis Rumiantsev
quelle
Ihre Guard-Anweisung bedeutet, dass UIGraphicsEndPDFContext () nicht aufgerufen wird - vielleicht früher einen Aufschub hinzufügen?
David H
@ DavidH danke, David, gute Idee! Ich denke auch, dass es eine gute Idee ist, einen Abschlussblock completion: (success: Bool) -> ()für
Wachrückfallfälle
1
Gestern habe ich ein Q & A gepostet, wie man ein hochauflösendes Bild erzeugt, indem man die Ansicht in einem großen Bild rendert
David H
5

Eine super einfache Möglichkeit, ein PDF aus UIView zu erstellen, ist die Verwendung der UIView-Erweiterung

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 ""
    }
  }
}

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

Ashish Chauhan
quelle
Danke, es hat funktioniert. Eine Frage. Ich habe also eine lange Bildlaufansicht, aber die PDF-Datei zeigt nur einen Teil davon. Gibt es also eine Möglichkeit, Ihren Code zu optimieren, um ihm beispielsweise die Höhe zu geben?
Hussein Elbeheiry
@HusseinElbeheiry Verwenden Sie einfach contentView, um PDF zu generieren. Wenn ich eine scrollView (UIScrollView) erstelle, werde ich definitiv eine contentView (UIView) erstellen und die contentView in die scrollView einfügen und alle nachfolgenden Elemente zur contentView hinzufügen. In diesem Fall reicht es aus, contentView zum Erstellen eines PDF-Dokuments zu verwenden. contentView.exportAsPdfFromView
iAleksandr
3

Mit Swift 5 / iOS 12 können Sie die Methode mit der Methode kombinieren CALayer, um eine PDF-Datei aus einer Instanz zu erstellen .render(in:)UIGraphicsPDFRendererwritePDF(to:withActions:)UIView


Der folgende Beispielcode für den Spielplatz zeigt, wie render(in:)und verwendet wird 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)")
}

Hinweis: Um auf playgroundSharedDataDirectoryIhrem Spielplatz verwendet werden zu können, müssen Sie zunächst einen Ordner mit dem Namen "Shared Playground Data" in Ihrem macOS-Ordner "Documents" erstellen.


Die folgende UIViewControllervollständige Implementierung der Unterklasse zeigt eine Möglichkeit, das vorherige Beispiel für eine iOS-App umzugestalten:

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

}
Imanou Petit
quelle
2

Dadurch wird PDF aus UIView generiert und der Druckdialog, Ziel C, geöffnet. - (IBAction)PrintPDF:(id)senderAn Ihre Schaltfläche auf dem Bildschirm anhängen . #import <QuartzCore/QuartzCore.h>Framework hinzufügen

H-Datei

    @interface YourViewController : UIViewController <MFMailComposeViewControllerDelegate,UIPrintInteractionControllerDelegate>

    {
    UIPrintInteractionController *printController;
    }

- (IBAction)PrintPDF:(id)sender;

M-Datei

-(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];

    }

}
magero
quelle
-4

Ich weiß nicht warum, aber die Antwort von casilic gibt mir unter iOS6.1 einen leeren Bildschirm. Der folgende Code funktioniert.

-(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;
}
Alex Stone
quelle
16
Dies ist der gleiche genaue Code wie meine Antwort, die gerade in zwei getrennten Methoden aufgeteilt wurde ???? Wie hat dies Ihr Problem mit dem leeren Bildschirm behoben, wenn es sich um denselben Code handelt?
Casillic
Ich hatte die gleiche Erfahrung. Ich habe ein leeres PDF vom ersten Code erhalten. Wenn Sie es wie Alex in zwei Teile teilen, funktioniert es. Kann nicht erklären warum.
Tom Tallak Solbu