【问题标题】:How to Convert UIView to PDF within iOS?如何在 iOS 中将 UIView 转换为 PDF?
【发布时间】:2011-07-23 12:35:54
【问题描述】:

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

例如,我有一个UIView,有像 Textviews、UILabelsUIImages 这样的子视图,那么如何将一个 big UIView 转换为一个整体,包括它的所有子视图以及 PDF 的子视图?

我已经检查了Apple's iOS reference。但是,它只涉及将文本/图像写入 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);
}

【问题讨论】:

    标签: ios pdf uiview uiimage core-graphics


    【解决方案1】:

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

    (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

    【讨论】:

    • +1 在找到这个简单的解决方案之前,我浏览了几篇关于 pdf 生成的帖子。
    • 我也想做同样的事情,你的方法似乎效果很好,但质量很低。我错过了什么吗?
    • 我怀疑质量非常低,因为它使用 UIView 并将其转换为光栅,而其他渲染文本和图像的方法直接将它们保存为 PDF 文件中的矢量。
    • 我正在遵循这种方法,但我得到了一个空白的 pdf 生成。谁能帮帮我?
    • 效果很好!!!干杯!!我唯一的问题是,它只在一页中生成 PDF。我怎样才能分隔页面而不是长 PDF 文件?!
    【解决方案2】:

    另外,如果有人感兴趣,这里是 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)
        }
    }
    

    【讨论】:

    • 只为首页创建 PDF!滚动视图呢?
    • 好问题!然而,我不是要问的人。也许开始另一个问题?
    • 我遇到了同样的问题,然后@SaurabhPrajapati 我创建了一个Question
    【解决方案3】:

    如果有人感兴趣,这里是 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() 没有被调用 - 也许更早添加一个延迟?
    • @DavidH 谢谢,大卫,好主意!另外,我认为,为保护返回案例添加一个完成块类型 completion: (success: Bool) -> () 是个好主意
    • 昨天我发布了一个关于如何通过在大图像中渲染视图然后将图像绘制成 PDF 来生成高分辨率图像的问答:stackoverflow.com/a/35442187/1633251
    【解决方案4】:

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

    斯威夫特 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 文件只显示了它的一部分,所以有没有办法调整你的代码,例如给它高度?
    • @HusseinElbeheiry 只需使用 contentView 生成 pdf。当我创建一个scrollView(UIScrollView)时,我肯定会创建一个contentView(UIView)并将contentView放在scrollView中,并将所有后续元素添加到contentView中。在这种情况下,使用 contentView 创建 PDF 文档就足够了。 contentView.exportAsPdfFromView
    • 在哪里可以找到保存的 PDF 文件?
    • @MidhunNarayan 在您的应用文档目录中。只需在控制台上打印 pdfPath 并访问它。
    • @AshishChauhan 我已经打印了路径,但是当我打开我的文件应用程序时它没有显示。我需要什么额外的东西才能在我的文件应用程序中查看转换后的 pdf
    【解决方案5】:

    使用 Swift 5 / iOS 12,您可以将 CALayerrender(in:) 方法与 UIGraphicsPDFRendererwritePDF(to:withActions:) 方法结合使用,以便从 UIView 实例创建 PDF 文件。


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

    注意:为了在您的 Playground 中使用 playgroundSharedDataDirectory,您首先需要在 macOS 的“Documents”文件夹中创建一个名为“Shared Playground Data”的文件夹。


    下面的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)")
            }
        }
    
    }
    

    【讨论】:

    • 在 iPhone 中我可以找到这个文件,使用文件应用程序它没有显示
    【解决方案6】:

    这将从 UIView 生成 PDF 并打开打印对话框,目标 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];
    
        }
    
    }
    

    【讨论】:

      【解决方案7】:

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

      【讨论】:

      • 这与我的答案完全相同,只是用两种不同的方法分解出来????当它是相同的代码时,你认为这如何解决了你的黑屏问题??
      • 我也有同样的经历。从第一个代码中得到一个空白 PDF。像亚历克斯那样把它分成两部分,让它工作。无法解释原因。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-25
      • 2016-12-08
      • 1970-01-01
      • 2011-07-07
      相关资源
      最近更新 更多