iOS에서 UIView를 PDF로 변환하는 방법은 무엇입니까?


87

앱에서 PDF를 표시하는 방법에 대한 많은 리소스가 UIView있습니다. 내가 지금 작업하고있는 것은에서 PDF를 만드는 것입니다 UIViews.

예를 들어, 나는이 UIViewTextviews 같은 파단으로,, UILabels, UIImages, 그래서 어떻게 변환 할 수있는 큰를 UIView PDF 파일에 모든 서브 뷰와 subsubviews을 포함한 전체?

내가 체크 한 애플의 아이폰 OS 참조 . 그러나 PDF 파일에 텍스트 / 이미지 조각을 쓰는 것에 대해서만 이야기합니다.

내가 직면 한 문제는 PDF로 파일에 쓰고 싶은 내용이 많다는 것입니다. 한 장씩 PDF로 작성하면 엄청난 작업이 될 것입니다. 이것이 제가 UIViewsPDF 또는 비트 맵 에 쓰는 방법을 찾고있는 이유 입니다.

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

7
UIView를 가져 와서 텍스트와 이미지를 렌더링하는 다른 방법이 PDF 파일의 벡터로 직접 보존하는 래스터로 변환하기 때문에 품질이 상당히 낮다고 생각합니다.
joshaidan

3
이 방법을 따르고 있지만 빈 pdf가 생성됩니다. 아무도 나를 도울 수 있습니까?
Raj

굉장하게 작동합니다 !!! 건배 !! 내가 가진 유일한 문제는 한 페이지에 PDF를 생성한다는 것입니다. 긴 PDF 파일 대신 페이지를 분리하려면 어떻게해야합니까?!
Rudi 2013 년

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

좋은 질문입니다! 그러나 나는 물어볼 사람이 아니다. 다른 질문을 시작할까요?
retrovius

나는 @SaurabhPrajapati 다음 같은 문제를했고 나는 만든 질문
조나스 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) -> ()가드 반환 경우에
데니스 Rumiantsev

1
어제 큰 이미지로보기를 렌더링 한 다음 관심있는 PDF로 이미지를 그려 고해상도 이미지를 생성하는 방법에 대한 Q & A를 게시했습니다. stackoverflow.com/a/35442187/1633251
David H

5

UIView에서 PDF를 만드는 아주 쉬운 방법은 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 / 아이폰 OS (12), 당신은 결합 할 수 CALayerrender(in:)과 방법을 UIGraphicsPDFRenderer의 ' writePDF(to:withActions:)a에서 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
이것은 두 가지 별도의 방법으로 나눈 내 대답과 동일한 코드입니다. ???? 같은 코드 일 때 빈 화면 문제가 어떻게 해결되었다고 생각하십니까 ??
casillic

저도 같은 경험을했습니다. 첫 번째 코드에서 빈 PDF를 얻었습니다. Alex가 한 것처럼 두 개로 나누면 작동했습니다. 이유를 설명 할 수 없습니다.
Tom Tallak Solbu 2015
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.