iOS内でUIViewをPDFに変換する方法


87

アプリのでPDFを表示する方法に関するリソースはたくさんありますUIView。私が現在取り組んでいるのは、からPDFを作成することUIViewsです。

例えば、私がしているUIView、Textviewsのようなサブビューで、UILabelsUIImages、ので、どのように私は、変換することができ、大きな UIView PDFにすべてのサブビューとsubsubviews含め全体としての?

AppleのiOSリファレンスを確認しました。ただし、テキスト/イメージの一部をPDFファイルに書き込むことについてのみ述べています。

私が直面している問題は、PDFとしてファイルに書き込みたいコンテンツが多いということです。PDFに1つずつ書き込むと、大変な作業になります。だからこそUIViews、PDFやビットマップに書き込む方法を探しています。

Stack Overflow内の他のQ / Aからコピーしたソースコードを試しました。ただし、UIView境界サイズの空白のPDFしか表示されません。

-(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 '10 / 10/30

7
UIViewを使用してラスタに変換しているため、品質がかなり低いと思います。テキストや画像をレンダリングする他の方法と同様に、PDFファイル内のベクトルとして直接保存します。
joshaidan

3
この方法に従っていますが、空白のPDFが生成されます。誰も私を助けることができますか?
Raj

それは素晴らしい作品です!!! 乾杯!! 私が抱えている唯一の問題は、PDFが1ページだけで生成されることです。長いPDFファイルを作成する代わりに、どうすればページを分割できますか?
Rudi

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
これは、firstPageのみのPDFを作成します!scrollviewはどうですか?
Saurabh Prajapati 2017年

すばらしい質問です。しかし、私は尋ねるべきではありません。おそらく別の質問を始めますか?
Retrovius 2017年

私は同じ問題を抱えており、@ SaurabhPrajapatiと私は質問
Jonas Deichelmann

10

誰かが興味を持っているなら、ここに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ありがとう、David、いいアイデアです!また、私は、completion: (success: Bool) -> ()ガードリターンケースに完了ブロックの種類を追加することをお勧めします
Denis Rumiantsev

1
昨日、ビューを大きな画像にレンダリングして高解像度の画像を作成し、その画像を興味のあるPDFに描画する方法に関するQ&Aを投稿しました:stackoverflow.com/a/35442187/1633251
David H

5

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

クレジット:http : //www.swiftdevcenter.com/create-pdf-from-uiview-wkwebview-and-uitableview/


おかげでうまくいきました、1つの質問、それで私は長いスクロールビューを持っていますが、PDFファイルはその一部しか表示しないので、たとえばそれに高さを与えるためにコードを微調整する方法はありますか?
フセインElbeheiry

@HusseinElbeheiryは、contentViewを使用してPDFを生成します。scrollView(UIScrollView)を作成するときは、必ずcontentView(UIView)を作成してcontentViewをscrollViewに配置し、後続のすべての要素をcontentViewに追加します。この場合、contentViewを使用してPDFドキュメントを作成するだけで十分です。contentView.exportAsPdfFromView
iAleksandr

3

スウィフト5 / iOSの12を使用すると、組み合わせることができますCALayerrender(in:)でメソッドをUIGraphicsPDFRendererS ' 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)")
}

注:playgroundSharedDataDirectoryPlaygroundで使用するには、まず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)")
        }
    }

}

2

これにより、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];

    }

}

-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
これは私の答えがちょうど2つの別々の方法で分割されたのとまったく同じコードですか???? それが同じコードであるときに、これがブランク画面の問題をどのように修正したと思いますか?
石灰質2013

私も同じ経験をしました。最初のコードから空白のPDFを取得しました。アレックスがしたようにそれを2つに分割し、それを機能させました。理由は説明できません。
Tom Tallak Solbu、2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.