วิธีการแปลง UIView เป็น PDF ภายใน iOS


88

มีจำนวนมากของทรัพยากรเกี่ยวกับวิธีการแสดงไฟล์ PDF ใน UIViewApp สิ่งที่ฉันทำงานในตอนนี้คือการสร้างรูปแบบไฟล์ PDF UIViewsจาก

ตัวอย่างเช่นผมมีUIViewกับ subviews เช่น Textviews, UILabels, UIImagesดังนั้นวิธีการที่ฉันสามารถแปลงใหญ่ UIViewเป็นทั้งรวมทั้ง subviews และ subsubviews มันเป็น PDF หรือไม่?

ฉันได้ตรวจสอบข้อมูลอ้างอิง iOS ของ Appleแล้ว อย่างไรก็ตามมันพูดถึงการเขียนข้อความ / รูปภาพลงในไฟล์ PDF เท่านั้น

ปัญหาที่ฉันพบคือเนื้อหาที่ฉันต้องการเขียนเป็นไฟล์ PDF มีจำนวนมาก ถ้าฉันเขียนลงใน PDF ทีละชิ้นมันจะเป็นงานใหญ่ที่ต้องทำ นั่นเป็นเหตุผลที่ฉันกำลังมองหาวิธีเขียนUIViewsลง PDF หรือแม้แต่บิตแมป

ฉันได้ลองซอร์สโค้ดที่ฉันคัดลอกมาจาก Q / A อื่น ๆ ภายใน Stack Overflow แต่ให้ PDF เปล่าที่มีUIViewขนาดขอบเขตเท่านั้น

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

คำตอบ:


124

โปรดทราบว่าวิธีการต่อไปนี้สร้างเพียงบิตแมปของมุมมอง มันไม่ได้สร้างตัวอักษรจริง

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

ตรวจสอบให้แน่ใจว่าคุณนำเข้า: QuartzCore / QuartzCore.h


2
+1 ฉันอ่านโพสต์ต่างๆเกี่ยวกับการสร้าง pdf ก่อนที่จะพบวิธีง่ายๆนี้
Jason George

7
ฉันต้องการทำเช่นเดียวกันและดูเหมือนว่าวิธีการของคุณจะใช้ได้ดี แต่คุณภาพต่ำมาก ฉันพลาดอะไรไปหรือเปล่า?
iEamin

7
ฉันสงสัยว่าคุณภาพค่อนข้างต่ำเพราะใช้ UIView และแปลงเป็นแรสเตอร์โดยที่วิธีอื่น ๆ ในการแสดงผลข้อความและรูปภาพจะเก็บรักษาไว้เป็นเวกเตอร์ในไฟล์ PDF โดยตรง
joshaidan

3
ฉันกำลังทำตามวิธีนี้ แต่ฉันได้รับ pdf เปล่าที่สร้างขึ้น มีใครช่วยฉันได้ไหม
Raj

ใช้งานได้ยอดเยี่ยม !!! ไชโย !! ปัญหาเดียวที่ฉันมีคือมันสร้าง PDF เพียงหน้าเดียว จะแยกหน้าแทนที่จะใช้ไฟล์ PDF ยาว ๆ ได้อย่างไร!
ฤดี

25

นอกจากนี้หากใครสนใจนี่คือรหัส 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
การสร้าง PDF สำหรับหน้าแรกเท่านั้น! แล้ว scrollview ล่ะ?
Saurabh Prajapati

คำถามดีมาก! ฉันไม่ใช่คนที่จะถามอย่างไรก็ตาม อาจจะเริ่มคำถามอื่น?
retrovius

ฉันมีปัญหาเดียวกันแล้ว @SaurabhPrajapati และฉันสร้างคำถาม
Jonas Deichelmann

11

หากมีคนสนใจนี่คือรหัส 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)
        }
    }

คำสั่งยามของคุณหมายความว่า UIGraphicsEndPDFContext () ไม่ได้ถูกเรียก - อาจเพิ่มการเลื่อนก่อนหน้านี้?
David H

@DavidH ขอบคุณเดวิดความคิดที่ดี! นอกจากนี้ฉันคิดว่ามีความคิดที่ดีที่จะเพิ่มชนิดของบล็อกที่สมบูรณ์completion: (success: Bool) -> ()สำหรับกรณีการส่งคืนยาม
Denis Rumiantsev

1
เมื่อวานนี้ฉันโพสต์ถาม - ตอบเกี่ยวกับวิธีสร้างภาพความละเอียดสูงโดยการแสดงผลมุมมองในภาพขนาดใหญ่จากนั้นวาดภาพเป็น PDF ในความสนใจ: stackoverflow.com/a/35442187/1633251
David H

5

วิธีที่ง่ายสุด ๆ ในการสร้าง PDF จาก UIView คือการใช้ UIView Extension

สวิฟต์ 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 ""
    }
  }
}

เครดิต: http://www.swiftdevcenter.com/create-pdf-from-uiview-wkwebview-and-uitableview/


ขอบคุณที่ใช้งานได้หนึ่งคำถามดังนั้นฉันจึงมีมุมมองแบบเลื่อนยาว แต่ไฟล์ PDF แสดงเพียงบางส่วนเท่านั้นดังนั้นจึงมีวิธีปรับแต่งโค้ดของคุณเช่นให้ความสูง
Hussein Elbeheiry

@HusseinElbeheiry เพียงแค่ใช้ contentView เพื่อสร้าง pdf เมื่อฉันสร้าง scrollView (UIScrollView) ฉันจะสร้าง contentView (UIView) และใส่ contentView ใน scrollView อย่างแน่นอนและฉันจะเพิ่มองค์ประกอบที่ตามมาทั้งหมดใน contentView ในกรณีนี้ก็เพียงพอที่จะใช้ contentView เพื่อสร้างเอกสาร PDF contentView.exportAsPdfFromView
iAleksandr

3

กับสวิฟท์ 5 / iOS 12 คุณสามารถรวมCALayerของrender(in:)วิธีการที่มีUIGraphicsPDFRenderer's writePDF(to:withActions:)วิธีการเพื่อที่จะสร้างไฟล์ PDF จากUIViewอินสแตนซ์


โค้ดตัวอย่าง Playground ต่อไปนี้แสดงวิธีใช้render(in:)และ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)")
}

หมายเหตุ: ในการใช้งานplaygroundSharedDataDirectoryใน Playground ของคุณก่อนอื่นคุณต้องสร้างโฟลเดอร์ชื่อ "Shared Playground Data" ในโฟลเดอร์ "Documents" ของ macOS


การUIViewControllerใช้งานคลาสย่อยที่สมบูรณ์ด้านล่างแสดงวิธีที่เป็นไปได้ในการปรับโครงสร้างตัวอย่างก่อนหน้าสำหรับแอป 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

สิ่งนี้จะสร้าง PDF จาก UIView และเปิดกล่องโต้ตอบการพิมพ์วัตถุประสงค์ C. แนบ- (IBAction)PrintPDF:(id)senderกับปุ่มของคุณบนหน้าจอ เพิ่ม#import <QuartzCore/QuartzCore.h>กรอบ

H ไฟล์

    @interface YourViewController : UIViewController <MFMailComposeViewControllerDelegate,UIPrintInteractionControllerDelegate>

    {
    UIPrintInteractionController *printController;
    }

- (IBAction)PrintPDF:(id)sender;

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

ฉันไม่รู้ว่าทำไม แต่คำตอบของ casilic ทำให้ฉันมีหน้าจอว่างเปล่าบน iOS6.1 โค้ดด้านล่างใช้งานได้

-(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
นี่เป็นรหัสเดียวกับคำตอบของฉันที่แยกออกเป็นสองวิธีแยกกัน ???? คุณคิดว่าวิธีนี้ช่วยแก้ปัญหาหน้าจอว่างของคุณได้อย่างไรเมื่อเป็นรหัสเดียวกัน ??
casillic

ฉันมีประสบการณ์เดียวกัน มี PDF เปล่าจากรหัสแรก การแยกมันออกเป็นสองส่วนเหมือนที่อเล็กซ์ทำทำให้มันใช้งานได้ อธิบายไม่ได้ว่าทำไม
Tom Tallak Solbu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.