如何在 iOS 中将 UIView 转换为 PDF?

有很多关于如何在应用程序的 UIView中显示 PDF 的资源。我现在的工作是从 UIViews创建一个 PDF。

例如,我有一个 UIView,子视图,如文本视图,UILabelsUIImages,所以我如何才能转换一个 很大 UIView作为一个整体,包括所有的子视图和子视图 PDF?

我已经检查了 苹果的 iOS 参考。然而,它只谈到写的文本/图像片段到一个 PDF 文件。

我面临的问题是,我想写成 PDF 文件的内容太多了。如果我把它们一块一块地写到 PDF 中,这将是一项巨大的工作。 这就是为什么我正在寻找一种方法来写 UIViews到 PDF 甚至位图。

我已经尝试了从 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);
}
55754 次浏览

Note that the following method creates just a bitmap of the view; it does not create actual typography.

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

Also make sure you import: QuartzCore/QuartzCore.h

I don't know why, but casilic's answer gives me blank screen on iOS6.1. The code below works.

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

If someone is interested, here's 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)
}
}

Additionally, if anyone is interested, here is the 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)
}
}

This will generate PDF from UIView and open print dialog, objective C. Attach - (IBAction)PrintPDF:(id)sender to your button on screen. Add #import <QuartzCore/QuartzCore.h> framework

H File

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


}


}

A super easy way to create a PDF from UIView is using UIView Extension

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

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

With Swift 5 / iOS 12, you can combine CALayer's render(in:) method with UIGraphicsPDFRenderer's writePDF(to:withActions:) method in order to create a PDF file from a UIView instance.


The following Playground sample code shows how to use render(in:) and 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)")
}

Note: in order to use playgroundSharedDataDirectory in your Playground, you first need to create a folder called "Shared Playground Data" in you macOS "Documents" folder.


The UIViewController subclass complete implementation below shows a possible way to refactor the previous example for an iOS app:

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


}