Trình xem PDF nhanh và gọn cho iPhone / iPad / iOs - mẹo và gợi ý?


369

Gần đây có rất nhiều câu hỏi về việc vẽ PDF.

Có, bạn có thể kết xuất PDF rất dễ dàng với UIWebViewnhưng không thể cung cấp hiệu suất và chức năng mà bạn mong đợi từ trình xem PDF tốt.

Bạn có thể vẽ một trang PDF thành CALayer hoặc UIImage . Apple thậm chí còn có mã mẫu để hiển thị cách vẽ một tệp PDF lớn trong UIScrollview có thể thu phóng

Nhưng các vấn đề tương tự tiếp tục cắt lên.

Phương pháp UIImage:

  1. PDF ở UIImagequy mô không tối ưu cũng như cách tiếp cận Lớp.
  2. CPU và bộ nhớ đánh vào việc tạo ra UIImagestừ một PDFcontext giới hạn / ngăn không cho sử dụng nó để tạo kết xuất thời gian thực của các mức thu phóng mới.

Phương thức CATiledLayer:

  1. Có một Chi phí đáng kể (thời gian) vẽ một trang PDF đầy đủ thành một CALayer: các ô riêng lẻ có thể được nhìn thấy kết xuất (ngay cả với một chỉnh sửa kích thước ô)
  2. CALayers Không thể chuẩn bị trước thời hạn (hiển thị ngoài màn hình).

Nói chung người xem PDF cũng khá nặng về bộ nhớ. Thậm chí giám sát việc sử dụng bộ nhớ của ví dụ PDF có thể phóng to của apple.

Trong dự án hiện tại của tôi, tôi đang phát triển trình xem PDF và hiển thị UIImagemột trang trong một luồng riêng (cũng có vấn đề ở đây!) Và trình bày nó trong khi tỷ lệ là x1. CATiledLayerđá kết xuất trong một lần tỷ lệ> 1. iBooks có cách tiếp cận kép tương tự như khi bạn cuộn các trang bạn có thể thấy phiên bản độ phân giải thấp hơn của trang chỉ trong chưa đầy một giây trước khi phiên bản sắc nét xuất hiện.

Tôi hiển thị 2 trang mỗi bên của trang trong tiêu điểm để hình ảnh PDF sẵn sàng che dấu lớp trước khi bắt đầu vẽ. Các trang bị hủy một lần nữa khi chúng cách +2 trang so với trang tập trung.

Có ai có bất kỳ hiểu biết nào, cho dù nhỏ hay rõ ràng để cải thiện hiệu suất / xử lý bộ nhớ của Bản vẽ PDF? hoặc bất kỳ vấn đề nào khác được thảo luận ở đây?

EDIT: Một số lời khuyên (Tín dụng- Luke Mcneice, VdesmedT, Matt Gallagher, Johann):

  • Lưu bất kỳ phương tiện truyền thông vào đĩa khi bạn có thể.

  • Sử dụng brickSizes lớn hơn nếu kết xuất trên TiledLayers

  • init các mảng được sử dụng thường xuyên với các đối tượng giữ chỗ, xen kẽ một cách tiếp cận thiết kế khác là cách này

  • Lưu ý rằng hình ảnh sẽ hiển thị nhanh hơn một CGPDFPageRef

  • Sử dụng NSOperationshoặc GCD & Blocks để chuẩn bị các trang trước thời hạn.

  • gọi CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);trước CGContextDrawPDFPageđể giảm mức sử dụng bộ nhớ trong khi vẽ

  • khởi tạo của bạn NSOperationsvới một docRef là một ý tưởng tồi (bộ nhớ), bọc docRef thành một singleton.

  • Hủy bỏ không cần thiết NSOperationsKhi bạn có thể, đặc biệt là nếu họ sẽ sử dụng bộ nhớ, hãy cẩn thận khi để bối cảnh mở!

  • Tái chế các đối tượng trang và hủy các khung nhìn không sử dụng

  • Đóng mọi bối cảnh mở ngay khi bạn không cần chúng

  • khi nhận được cảnh báo bộ nhớ phát hành và tải lại DocRef và bất kỳ trang Caches nào

Các tính năng PDF khác:

Tài liệu

Dự án ví dụ

  • Apple / ZoomingPDF - thu phóng UIScrollView,,CATiledLayer
  • vfr / reader - phóng to, phân trang , UIScrollView.CATiledView
  • trán / lá - phân trang với chuyển tiếp tốt đẹp
  • / skim - mọi thứ có vẻ như (trình đọc / chỉnh sửa PDF cho OSX)

bình luận để đảm bảo peeps nhận được thông báo chỉnh sửa
Luke Mcneice

+1 và cảm ơn vì đã thêm tất cả thông tin này, ước gì tôi có nó khi tôi đang phát triển trình đọc của mình;) cũng cảm ơn vì đã thêm câu hỏi của tôi về chú thích PDF (nó cũng chứa câu trả lời với mã mẫu). Cách đây vài ngày, tôi đã mở cái này: stackoverflow.com/questions/4097044/pdf-search-on-the-iphone bạn có lời khuyên nào không?
pt2ph8

Tôi chưa bao gồm điều này cho bản thân mình vì vậy tôi không thể nói bất cứ điều gì ngoài việc đưa bạn đến blog ý tưởng ngẫu nhiên: Random-ideas.net/posts/42 Cảm ơn vì bài đăng, tôi đang cố gắng thu thập tất cả các vấn đề PDF trong một địa điểm.
Luke Mcneice

8
Tại công ty của tôi, chúng tôi đã sử dụng để kết xuất Pdf, ký hiệu, v.v., một giải pháp của bên thứ 3 được gọi PSPDFKit, nó không rẻ, nhưng đáng giá: pspdfkit.com
János

1
+1 Tôi đã làm theo những lời khuyên hữu ích này cho người xem pdf mã nguồn mở Swifty PDF github.com/prcela/SwiftyPDF
Prcela

Câu trả lời:


103

Tôi đã xây dựng loại ứng dụng như vậy bằng cách sử dụng xấp xỉ cùng một cách tiếp cận ngoại trừ:

  • Tôi lưu trữ hình ảnh được tạo trên đĩa và luôn tạo trước hai đến ba hình ảnh trong một luồng riêng biệt.
  • Tôi không phủ lớp UIImagemà thay vào đó, hãy vẽ hình ảnh trong lớp khi thu phóng là 1. Các ô đó sẽ được giải phóng tự động khi cảnh báo bộ nhớ được đưa ra.

Bất cứ khi nào người dùng bắt đầu phóng to, tôi có được CGPDFPagevà hiển thị nó bằng CTM thích hợp. Mã trong - (void)drawLayer: (CALayer*)layer inContext: (CGContextRef) contextgiống như:

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

vấn đề là đối tượng chống lại CGPDFDocumentRef. Tôi đồng bộ hóa phần mà tôi truy cập vào thuộc pdfDoctính vì tôi giải phóng nó và tạo lại nó khi nhận được memoryWarnings. Có vẻ như CGPDFDocumentRefđối tượng thực hiện một số bộ nhớ đệm nội bộ mà tôi không tìm thấy cách loại bỏ.


1
Cách tiếp cận của bạn khi người dùng bắt đầu Thu phóng là gì?
Luke Mcneice

2
@Luke: Tôi đã sửa đổi bài đăng để trả lời
VdesmedT

1
Cảm ơn rất nhiều vì điều này và lời khuyên về bộ nhớ đệm CGPDFDocumentRef.
Luke Mcneice

Chỉ cần một câu hỏi nhanh: tại sao bạn nhận được pdfPageRef và biến đổi khi tỷ lệ là 1.0? bởi vì tôi thấy rằng bạn vẽ một BaseImage (hình ảnh từ bg worker?) trước khi bạn tạo biến đổi.
Luke Mcneice

@Luke: Đăng ở đây bất kỳ cải thiện hiệu suất nào bạn có thể thực hiện. Cảm ơn bạn !
VdesmedT

67

Để trình xem PDF đơn giản và hiệu quả, khi bạn chỉ yêu cầu chức năng giới hạn, giờ đây bạn có thể (iOS 4.0+) sử dụng khung QuickLook:

Trước tiên, bạn cần liên kết với QuickLook.framework#import <QuickLook/QuickLook.h>;

Sau đó, trong một viewDidLoadhoặc bất kỳ phương thức khởi tạo lười biếng nào:

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

Vâng, về mặt khái niệm này giống như sử dụng UIWebView. Chúng tôi sẽ không dành hàng giờ để tìm ra cách sử dụng công cụ CGPDF * nếu việc đó dễ dàng.
pt2ph8

5
Ừ chắc chắn. Tôi chắc chắn không trình bày nó như là một giải pháp hoàn chỉnh. Nhưng một số độc giả của câu hỏi này có thể không nhận thức được rằng, đối với một số mục đích, đây là một giải pháp hợp lý. Cập nhật câu trả lời để làm rõ điều đó.
Joshua J. McKinnon

6
+1 cho điều này. Sở thích chính của tôi là cho phép người dùng xem một số tài liệu trong ứng dụng ở định dạng PDF. Tôi không quan tâm đến việc tìm kiếm, đánh dấu hoặc bất kỳ tiếng chuông và còi nào khác - chỉ hiển thị PDF biểu diễn.
memmons

2
Danh mục UIImage + PDF của tôi là trình kết xuất PDF nhanh không vô nghĩa với lớp lưu trữ được tích hợp sẵn. github.com/mindbrix/UIImage-PDF
Mindbrix

6

Kể từ iOS 11 , bạn có thể sử dụng khung gốc có tên PDFKit để hiển thị và thao tác với các tệp PDF.

Sau khi nhập PDFKit, bạn nên khởi tạo một PDFViewURL cục bộ hoặc từ xa và hiển thị nó trong chế độ xem của bạn.

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

Đọc thêm về PDFKit trong tài liệu Nhà phát triển của Apple.


-2
 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);
     } }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.