[ios] iPhone / iPad / IO를위한 빠르고 마른 PDF 뷰어-팁과 힌트?

최근에 PDF 그리기에 대한 많은 질문이있었습니다.

그렇습니다. PDF를 아주 쉽게 렌더링 할 수는 UIWebView있지만 훌륭한 PDF 뷰어에서 기대할 수있는 성능과 기능을 제공 할 수는 없습니다.

PDF 페이지 를 CALayer 또는 UIImage에 그릴 수 있습니다 . Apple 은 Zoomable UIScrollview에서 큰 PDF 그리는 방법을 보여주는 샘플 코드도 있습니다.

그러나 같은 문제가 계속 자랍니다.

UIImage 메소드 :

  1. PDF UIImage는 레이어 방식뿐만 아니라 광학적으로 확장되지 않습니다.
  2. CPU 및 메모리
    는 새로운 줌 레벨의 실시간 렌더링을 생성하기 위해이를 사용하여 한계 / 예방 UIImages으로부터 생성에 도달했습니다 PDFcontext.

CATiledLayer 방법 :

  1. 전체 PDF 페이지를 다음으로 그리기위한 상당한 오버 헤드 (시간) CALayer가 있습니다 : 개별 타일이 렌더링되는 것을 볼 수 있습니다 (타일 크기 조정을 사용하더라도)
  2. CALayers 미리 준비 할 수 없습니다 (스크린 외부에서 렌더링).

일반적으로 PDF 뷰어는 메모리가 상당히 무겁습니다. 애플의 줌이 가능한 PDF 예제의 메모리 사용량도 모니터링하십시오.

현재 프로젝트에서 PDF 뷰어를 개발 중이며 UIImage페이지를 별도의 스레드로 렌더링하고 (여기에서도 문제가 있습니다!) 크기가 x1 인 동안 표시합니다. CATiledLayer스케일이 1보다 크면 렌더링이 시작됩니다. iBooks는 페이지를 스크롤 할 때와 비슷한 이중 테이크 접근 방식을 사용하여 선명한 버전이 나타나기 전에 1 초 이내에 페이지의 저해상도 버전을 볼 수 있습니다.

Im은 PDF 이미지가 도면을 시작하기 전에 레이어를 마스킹 할 준비가되도록 페이지의 각면에 2 페이지 씩 초점을 맞 춥니 다.

그림 PDF의 성능 / 메모리 처리를 개선하기 위해 작거나 명백한 사람에 대한 통찰력이 있습니까? 또는 여기에 논의 된 다른 문제가 있습니까?

편집 : 몇 가지 팁 (Credit-Luke Mcneice, VdesmedT, Matt Gallagher, Johann) :

  • 가능하면 미디어를 디스크에 저장하십시오.

  • TiledLayers에서 렌더링하는 경우 더 큰 tileSizes를 사용하십시오.

  • 초기화 자주 자리 객체와 배열을 사용 alternitively 다른 설계 방식은 이것

  • 이미지는 a보다 빠르게 렌더링됩니다. CGPDFPageRef

  • NSOperations또는 GCD 및 블록 을 사용 하여 미리 페이지를 준비하십시오.

  • 그리기 중 메모리 사용량을 줄이기 위해 전화 CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);하기CGContextDrawPDFPage

  • docRef로 당신 NSOperations을 초기화하는 것은 나쁜 생각 (메모리)입니다 .docRef를 싱글 톤으로 감싸십시오.

  • 불필요하게 취소 NSOperations할 수 있습니다. 특히 메모리를 사용하는 경우 컨텍스트를 열어 두지 마십시오!

  • 페이지 개체를 재활용하고 사용하지 않는보기를 삭제하십시오.

  • 필요없는 즉시 열린 컨텍스트를 모두 닫으십시오.

  • 메모리 경고를 받고 DocRef 및 모든 페이지 캐시를 다시로드

다른 PDF 기능 :

선적 서류 비치

프로젝트 예



답변

나는 다음과 같은 것을 제외하고는 거의 동일한 접근법을 사용하여 이러한 종류의 응용 프로그램을 작성했습니다.

  • 생성 된 이미지를 디스크에 캐시하고 항상 별도의 스레드에서 2 ~ 3 개의 이미지를 미리 생성합니다.
  • UIImage확대 / 축소가 1 일 때 오버레이가 아니라 대신 레이어에 이미지를 그립니다. 메모리 경고가 발생하면 해당 타일이 자동으로 해제됩니다.

사용자가 확대 / 축소를 시작할 때마다 CGPDFPage적절한 CTM을 사용하여 이미지를 렌더링합니다. 코드 - (void)drawLayer: (CALayer*)layer inContext: (CGContextRef) context는 다음과 같습니다.

CGAffineTransform currentCTM = CGContextGetCTM(context);
if (currentCTM.a == 1.0 && baseImage) {
    //Calculate ideal scale
    CGFloat scaleForWidth = baseImage.size.width/self.bounds.size.width;
    CGFloat scaleForHeight = baseImage.size.height/self.bounds.size.height;
    CGFloat imageScaleFactor = MAX(scaleForWidth, scaleForHeight);

    CGSize imageSize = CGSizeMake(baseImage.size.width/imageScaleFactor, baseImage.size.height/imageScaleFactor);
    CGRect imageRect = CGRectMake((self.bounds.size.width-imageSize.width)/2, (self.bounds.size.height-imageSize.height)/2, imageSize.width, imageSize.height);
    CGContextDrawImage(context, imageRect, [baseImage CGImage]);
} else {
    @synchronized(issue) {
        CGPDFPageRef pdfPage = CGPDFDocumentGetPage(issue.pdfDoc, pageIndex+1);
        pdfToPageTransform = CGPDFPageGetDrawingTransform(pdfPage, kCGPDFMediaBox, layer.bounds, 0, true);
        CGContextConcatCTM(context, pdfToPageTransform);
        CGContextDrawPDFPage(context, pdfPage);
    }
}

문제는 개체를 포함합니다 CGPDFDocumentRef. pdfDocmemoryWarnings를받을 때 속성을 해제하고 다시 작성하기 때문에 속성에 액세스하는 부분을 동기화합니다 . CGPDFDocumentRef객체가 제거하는 방법을 찾지 못한 내부 캐싱을 수행 하는 것 같습니다 .


답변

간단하고 효과적인 PDF 뷰어를 위해 제한된 기능 만 필요한 경우 이제 QuickLook 프레임 워크를 사용할 수 있습니다 (iOS 4.0 이상).

먼저 QuickLook.framework#import
<QuickLook/QuickLook.h>;

나중에, viewDidLoad지연 초기화 방법 중 하나 또는 하나에서 :

QLPreviewController *previewController = [[QLPreviewController alloc] init];
previewController.dataSource = self;
previewController.delegate = self;
previewController.currentPreviewItemIndex = indexPath.row;
[self presentModalViewController:previewController animated:YES];
[previewController release];


답변

iOS 11 부터 PDF를 표시하고 조작하기 위해 PDFKit 이라는 기본 프레임 워크를 사용할 수 있습니다 .

PDFKit를 가져온 후에 PDFView는 로컬 또는 원격 URL로를 초기화 하여보기에 표시해야합니다.

if let url = Bundle.main.url(forResource: "example", withExtension: "pdf") {
    let pdfView = PDFView(frame: view.frame)
    pdfView.document = PDFDocument(url: url)
    view.addSubview(pdfView)
}

Apple Developer documentation에서 PDFKit에 대해 더 읽으십시오.


답변

 CGAffineTransform currentCTM = CGContextGetCTM(context);     if
 (currentCTM.a == 1.0 && baseImage)  {
     //Calculate ideal scale
     CGFloat scaleForWidth = baseImage.size.width/self.bounds.size.width;
     CGFloat scaleForHeight = baseImage.size.height/self.bounds.size.height;
     CGFloat imageScaleFactor = MAX(scaleForWidth, scaleForHeight);
     CGSize imageSize = CGSizeMake(baseImage.size.width/imageScaleFactor,
 baseImage.size.height/imageScaleFactor);
     CGRect imageRect = CGRectMake((self.bounds.size.width-imageSize.width)/2,
 (self.bounds.size.height-imageSize.height)/2, imageSize.width,
 imageSize.height);
     CGContextDrawImage(context, imageRect, [baseImage CGImage]); }  else  {
     @synchronized(issue)  {
         CGPDFPageRef pdfPage = CGPDFDocumentGetPage(issue.pdfDoc, pageIndex+1);
         pdfToPageTransform = CGPDFPageGetDrawingTransform(pdfPage, kCGPDFMediaBox, layer.bounds, 0, true);
         CGContextConcatCTM(context, pdfToPageTransform);
         CGContextDrawPDFPage(context, pdfPage);
     } }


답변