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

如何在iOS内将UIView转换为PDF?,ios,pdf,uiview,uiimage,core-graphics,Ios,Pdf,Uiview,Uiimage,Core Graphics,关于如何在应用程序的UIView中显示PDF,有很多参考资料。我现在正在做的是从UIViews创建一个PDF 例如,我有一个UIView,它的子视图有文本视图、UILabels、UIImages,那么如何将一个大的UIView作为一个整体,包括它的所有子视图和子视图转换成PDF 我查过了。但是,它只涉及将文本/图像片段写入PDF文件 我面临的问题是,我想要以PDF格式写入文件的内容很多。如果我把它们一块一块地写进PDF,那将是一项艰巨的工作。 这就是为什么我在寻找一种将UIViews写入PDF甚

关于如何在应用程序的
UIView
中显示PDF,有很多参考资料。我现在正在做的是从
UIViews
创建一个PDF

例如,我有一个
UIView
,它的子视图有文本视图、
UILabels
UIImages
,那么如何将一个大的
UIView
作为一个整体,包括它的所有子视图和子视图转换成PDF

我查过了。但是,它只涉及将文本/图像片段写入PDF文件

我面临的问题是,我想要以PDF格式写入文件的内容很多。如果我把它们一块一块地写进PDF,那将是一项艰巨的工作。 这就是为什么我在寻找一种将
UIViews
写入PDF甚至位图的方法

我已经在堆栈溢出中尝试了从其他Q/A复制的源代码。但它只给我一个空白的PDF,大小为
UIView
bounds

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

请注意,以下方法仅创建视图的位图;它不会创建实际的排版

(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

我不知道为什么,但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;
}

如果有人感兴趣,以下是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)
        }
    }

此外,如果有人感兴趣,以下是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)
    }
}

这将从UIView生成PDF,并打开打印对话框objective C。 将
-(iAction)PrintPDF:(id)发送者
附加到屏幕上的按钮上。 添加
#导入
框架

H文件

    @interface YourViewController : UIViewController <MFMailComposeViewControllerDelegate,UIPrintInteractionControllerDelegate>

    {
    UIPrintInteractionController *printController;
    }

- (IBAction)PrintPDF:(id)sender;

从UIView创建PDF的一种超级简单的方法是使用UIView扩展

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

信用证:

使用Swift 5/iOS 12,您可以将
CALayer
的方法与
UIGraphicsPFrenderer
的方法相结合,以便从
UIView
实例创建PDF文件


下面的示例代码显示了如何使用
render(in:)
writePDF(to:withActions:)

注意:为了在您的游乐场中使用,您首先需要在macOS“文档”文件夹中创建一个名为“共享游乐场数据”的文件夹


下面的
UIViewController
子类complete implementation显示了一种重构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)")
        }
    }

}

+在找到这个简单的解决方案之前,我浏览了好几篇关于pdf生成的文章。我也想做同样的事情,你的方法似乎很好,但质量很低。我错过了什么吗?我怀疑质量很低,因为它采用UIView并将其转换为光栅,而其他渲染文本和图像的方法直接将它们作为矢量保存在PDF文件中。我遵循此方法,但生成的是一个空白PDF。有人能帮我吗?真是太棒了!!!干杯我唯一的问题是,它只在一个页面中生成PDF。我怎样才能把页面分开,而不是有一个很长的PDF文件?!这与我刚才用两种不同的方法给出的答案完全相同????你认为这是如何解决你的黑屏问题时,它是相同的代码??我有同样的经验。从第一个代码中获取一个空白PDF。像亚历克斯那样一分为二,成功了。无法解释原因。您的guard语句意味着未调用UIGraphicsSendPdfContext()-可能会提前添加延迟?@DavidH谢谢,David,好主意!此外,我认为,有一个好主意,为警卫返回案例添加一个完成块类
完成:(success:Bool)->()
。昨天,我发布了一个问答,介绍了如何通过在大图像中渲染视图,然后感兴趣地将图像绘制成PDF来生成高分辨率图像:这只为第一页创建PDF!scrollview呢?好问题!然而,我不是该问的人。也许开始另一个问题?我有同样的问题,然后@SaurabhPrajapati和我创建了一个感谢它工作,一个问题,所以我有长滚动视图,但PDF文件只显示了它的一部分,所以有没有办法调整代码,例如给它高度?@HusseinElbeheiry只使用contentView生成PDF。当我创建一个scrollView(UIScrollView)时,我肯定会创建一个contentView(UIView)并将contentView放在scrollView中,然后我会将所有后续元素添加到contentView中。在这种情况下,使用contentView创建PDF文档就足够了。contentView.ExportAsPDFromView
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)")
}
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)")
        }
    }

}