İOS içinde UIView PDF'ye nasıl dönüştürülür?


88

Bir Uygulamada PDF'nin nasıl görüntüleneceği hakkında birçok kaynak vardır UIView. Şu anda üzerinde çalıştığım şey bir PDF oluşturmaktır UIViews.

Örneğin, bir var UIViewTextView'lar gibi subviews ile, UILabels, UIImages, peki nasıl bir dönüştürebilir Big UIView PDF için tüm subviews ve subsubviews dahil bir bütün olarak?

Apple'ın iOS referansını kontrol ettim . Ancak, yalnızca metin / resim parçalarını bir PDF dosyasına yazmaktan bahsediyor.

Karşılaştığım sorun, bir dosyaya PDF olarak yazmak istediğim içeriğin çok fazla olması. Bunları PDF'ye parça parça yazarsam, yapılacak çok iş olacak. Bu yüzden UIViewsPDF'lere ve hatta bitmap'lere yazmanın bir yolunu arıyorum .

Stack Overflow'da diğer Q / A'dan kopyaladığım kaynak kodunu denedim. Ancak bana yalnızca UIViewsınır boyutunda boş bir PDF veriyor .

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

Yanıtlar:


124

Aşağıdaki yöntemin , görünümün yalnızca bir bit eşlemini oluşturduğunu unutmayın ; gerçek tipografi yaratmaz.

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

Ayrıca şunları içe aktardığınızdan emin olun: QuartzCore / QuartzCore.h


2
+1 Bu basit çözümü bulmadan önce pdf oluşturmayla ilgili birkaç gönderiye baktım.
Jason George

7
Ben de aynısını yapmak istedim ve yönteminiz iyi çalışıyor gibi görünüyor ancak kalitesi çok düşük. Bir şey mi kaçırdım?
iEamin

7
Kalitenin oldukça düşük olduğundan şüpheleniyorum çünkü UIView'ı alıyor ve onu bir raster'e dönüştürüyor, burada diğer metin ve görüntü işleme yöntemleri onları doğrudan PDF dosyasında vektör olarak koruyor.
joshaidan

3
Bu yöntemi takip ediyorum, ancak oluşturulan boş bir pdf alıyorum. biri bana yardım edebilir mi ?
Raj

Harika çalışıyor !!! şerefe !! Tek sorunum, sadece bir sayfada PDF oluşturması. Uzun bir PDF dosyası yerine sayfaları nasıl ayırabilirim?
Rudi

25

Ek olarak, ilgilenen varsa Swift 3 kodu:

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
bu sadece ilk sayfa için PDF oluşturuyor! scrollview ne olacak?
Saurabh Prajapati

Harika soru! Ancak soracak kişi ben değilim. Belki başka bir soru başlatır mısınız?
retrovius

O zaman da aynı sorunu yaşıyorum @SaurabhPrajapati ve bir Soru
Jonas Deichelmann

11

İlgilenen biri varsa Swift 2.1 kodunu burada bulabilirsiniz:

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

Guard ifadeniz UIGraphicsEndPDFContext () çağrılmadığı anlamına gelir - belki daha önce bir erteleme ekleyebilir misiniz?
David H

@DavidH teşekkürler David, iyi fikir! Ayrıca, completion: (success: Bool) -> ()nöbet iade davaları için bir tamamlama bloğu türü eklemek için iyi bir fikir olduğunu düşünüyorum
Denis Rumantsev

1
Dün, görünümü büyük bir görüntüde
David H

5

UIView'den bir PDF oluşturmanın süper kolay bir yolu, UIView Uzantısını kullanmaktır

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

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


Teşekkürler işe yaradı, Bir soru, Bu yüzden uzun kaydırma görünümüm var, ancak PDF dosyası yalnızca bir bölümünü gösteriyor, bu nedenle kodunuzu örneğin Yükseklik vermek için değiştirmenin bir yolu var mı?
Hussein Elbeheiry

@HusseinElbeheiry, pdf oluşturmak için sadece contentView kullanın. Bir scrollView (UIScrollView) oluşturduğumda, kesinlikle bir contentView (UIView) oluşturacağım ve contentView'i scrollView'a koyacağım ve sonraki tüm öğeleri contentView'e ekleyeceğim. Bu durumda, bir PDF belgesi oluşturmak için contentView kullanmak yeterlidir. contentView.exportAsPdfFromView
iAleksandr

3

Swift 5 / iOS 12 ile, birleştirebilirsiniz CALayer'ın render(in:)yöntemi ile UIGraphicsPDFRenderers' writePDF(to:withActions:)bir PDF dosyası oluşturmak için yöntemin UIViewörneği.


Aşağıdaki Playground örnek kodu nasıl kullanılacağını gösterir render(in:)ve 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)")
}

Not: playgroundSharedDataDirectoryOyun Alanınızda kullanmak için önce macOS "Belgeler" klasörünüzde "Paylaşılan Oyun Alanı Verileri" adlı bir klasör oluşturmanız gerekir.


Aşağıdaki UIViewControllertam alt sınıf uygulaması, bir iOS uygulaması için önceki örneği yeniden düzenlemenin olası bir yolunu gösterir:

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

Bu, UIView'den PDF oluşturacak ve yazdırma iletişim kutusunu açacaktır, hedef C. - (IBAction)PrintPDF:(id)senderEkran üzerindeki düğmenize ekleyin . #import <QuartzCore/QuartzCore.h>Çerçeve ekle

H Dosyası

    @interface YourViewController : UIViewController <MFMailComposeViewControllerDelegate,UIPrintInteractionControllerDelegate>

    {
    UIPrintInteractionController *printController;
    }

- (IBAction)PrintPDF:(id)sender;

M Dosyası

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

Neden bilmiyorum ama casilic'in cevabı bana iOS6.1'de boş ekran veriyor. Aşağıdaki kod çalışır.

-(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
Bu, cevabımın iki ayrı yöntemde bölünmüş haliyle aynı koddur ???? Aynı kod olduğunda bunun boş ekran sorununuzu nasıl çözdüğünü düşünüyorsunuz?
2013

Aynı deneyime sahibim. İlk koddan boş bir PDF aldım. Alex'in yaptığı gibi ikiye bölmek işe yaradı. Nedenini açıklayamıyorum.
Tom Tallak Solbu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.