Bagaimana cara mengkonversi UIView ke PDF di iOS?

88

Ada banyak sumber daya tentang cara menampilkan PDF di Aplikasi UIView. Apa yang saya kerjakan sekarang adalah membuat PDF dari UIViews.

Sebagai contoh, saya memiliki UIView, dengan subviews seperti TextViews, UILabels, UIImages, jadi bagaimana bisa saya mengkonversi besar UIView secara keseluruhan termasuk semua subviews dan subsubviews ke PDF?

Saya telah memeriksa referensi iOS Apple . Namun, ini hanya berbicara tentang menulis potongan teks / gambar ke file PDF.

Masalah yang saya hadapi adalah banyak konten yang ingin saya tulis ke file sebagai PDF. Jika saya menuliskannya ke PDF sepotong demi sepotong, itu akan menjadi pekerjaan besar yang harus dilakukan. Itulah mengapa saya mencari cara untuk menulis UIViewske PDF atau bahkan bitmap.

Saya telah mencoba kode sumber yang saya salin dari Q / A lain dalam Stack Overflow. Tapi itu hanya memberi saya PDF kosong dengan UIViewukuran batas.

-(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
sumber

Jawaban:

124

Perhatikan bahwa metode berikut hanya membuat bitmap tampilan; itu tidak menciptakan tipografi yang sebenarnya.

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

Pastikan juga Anda mengimpor: QuartzCore / QuartzCore.h

casillic
sumber
2
+1 Saya membaca beberapa posting tentang pembuatan pdf sebelum menemukan solusi sederhana ini.
Jason George
7
Saya ingin melakukan hal yang sama dan metode Anda tampaknya berfungsi dengan baik tetapi kualitasnya sangat rendah. Apakah saya melewatkan sesuatu?
iEamin
7
Saya menduga kualitasnya cukup rendah karena menggunakan UIView dan mengubahnya menjadi raster, sedangkan metode rendering teks dan gambar lainnya secara langsung menyimpannya sebagai vektor dalam file PDF.
joshaidan
3
Saya mengikuti metode ini, tetapi saya mendapatkan pdf kosong yang dihasilkan. ada yang bisa bantu saya ?
Raj
Ini bekerja luar biasa !!! Bersulang !! satu-satunya masalah yang saya miliki adalah, ini menghasilkan PDF hanya dalam satu halaman. Bagaimana saya bisa memisahkan halaman daripada memiliki file PDF yang panjang ?!
Rudi
25

Selain itu, jika ada yang tertarik, berikut adalah kode 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)
    }
}
retrovius
sumber
1
ini membuat PDF hanya untuk firstPage! bagaimana dengan scrollview?
Saurabh Prajapati
Pertanyaan bagus! Namun, saya bukan orang yang bertanya. Mungkin memulai pertanyaan lain?
retrovius
Saya memiliki masalah yang sama, maka @SaurabhPrajapati dan saya membuat Pertanyaan
Jonas Deichelmann
11

Jika seseorang tertarik, inilah kode 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)
        }
    }
Denis Rumiantsev
sumber
Pernyataan penjaga Anda berarti UIGraphicsEndPDFContext () tidak dipanggil - mungkin tambahkan penundaan lebih awal?
David H
@DavidH terima kasih, David, ide bagus! Juga, saya pikir, ada baiknya menambahkan jenis blok penyelesaian completion: (success: Bool) -> ()untuk kasus pengembalian penjaga
Denis Rumiantsev
1
Kemarin saya memposting Tanya Jawab tentang cara menghasilkan gambar resolusi tinggi dengan menampilkan tampilan dalam gambar besar, lalu menggambar gambar tersebut menjadi PDF yang tertarik: stackoverflow.com/a/35442187/1633251
David H
5

Cara super mudah untuk membuat PDF dari UIView adalah menggunakan Ekstensi UIView

Swift 4.2.0

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

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

Ashish Chauhan
sumber
Terima kasih berhasil, Satu pertanyaan, Jadi saya memiliki tampilan gulir yang panjang, tetapi file PDF hanya menampilkan sebagian saja, jadi apakah ada cara untuk mengubah kode Anda misalnya dengan memberikan Ketinggian?
Hussein Elbeheiry
@HusseinElbeheiry cukup gunakan contentView untuk menghasilkan pdf. Ketika saya membuat scrollView (UIScrollView), saya pasti akan membuat contentView (UIView) dan meletakkan contentView di scrollView, dan saya menambahkan semua elemen berikutnya ke contentView. Dalam hal ini, cukup menggunakan contentView untuk membuat dokumen PDF. contentView.exportAsPdfFromView
iAleksandr
3

Dengan Swift 5 / iOS 12, Anda dapat menggabungkan CALayer's render(in:)metode dengan UIGraphicsPDFRenderer' s writePDF(to:withActions:)metode untuk membuat file PDF dari UIViewcontoh.


Kode contoh Playground berikut menunjukkan cara menggunakan render(in:)dan 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)")
}

Catatan: untuk menggunakan playgroundSharedDataDirectoryPlayground, Anda harus terlebih dahulu membuat folder bernama "Shared Playground Data" di folder "Documents" macOS Anda.


The UIViewControllerimplementasi lengkap subclass di bawah ini menunjukkan sebuah cara yang mungkin untuk refactor contoh sebelumnya untuk aplikasi 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)")
        }
    }

}
Imanou Petit
sumber
2

Ini akan menghasilkan PDF dari UIView dan membuka dialog cetak, tujuan C. Lampirkan - (IBAction)PrintPDF:(id)sendertombol Anda di layar. Tambahkan #import <QuartzCore/QuartzCore.h>kerangka kerja

File H

    @interface YourViewController : UIViewController <MFMailComposeViewControllerDelegate,UIPrintInteractionControllerDelegate>

    {
    UIPrintInteractionController *printController;
    }

- (IBAction)PrintPDF:(id)sender;

M File

-(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
sumber
-4

Saya tidak tahu mengapa, tetapi jawaban casilic memberi saya layar kosong di iOS6.1. Kode di bawah berfungsi.

-(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
sumber
16
Ini adalah kode yang sama persis dengan jawaban saya yang baru saja dipecah dalam dua metode terpisah ???? Bagaimana menurut Anda ini memperbaiki masalah layar kosong Anda ketika itu adalah kode yang sama ??
casillic
Saya memiliki pengalaman yang sama. Punya PDF kosong dari kode pertama. Membaginya menjadi dua seperti yang dilakukan Alex, membuatnya berhasil. Tidak bisa menjelaskan kenapa.
Tom Tallak Solbu