Швидкий і худорлявий переглядач PDF для iPhone / iPad / iOs - поради та підказки?


368

Останнім часом було багато запитань щодо малювання PDF-файлів.

Так, ви можете візуалізувати PDF дуже легко за допомогою, UIWebViewале це не може забезпечити ефективність та функціональність, яких ви очікували від хорошого переглядача PDF.

Ви можете намалювати сторінку PDF в CALayer або до UIImage . Apple навіть має зразок коду, щоб показати, як намалювати великий PDF в Zoomable UIScrollview

Але ті самі питання продовжують з'являтися.

Метод UIImage:

  1. PDF-файли UIImageне в оптичному масштабі, а також підході шару.
  2. Процесор і пам'ять вдарив по породжує UIImagesвід А PDFcontext лімітів / НЕ дозволяє використовувати його для створення в реальному часі візуалізації нових зум-рівнів.

Метод CATiledLayer:

  1. Існує значне накладне (час) малювання повної сторінки PDF на CALayer: окремі плитки можна побачити візуалізацію (навіть при налаштуванні плитки розміру)
  2. CALayers не можу бути готовим достроково (відображається поза екраном).

Як правило, глядачі PDF також досить важкі для пам'яті. Навіть слідкуйте за використанням пам'яті яблучного масштабного прикладу PDF.

У своєму поточному проекті я розробляю програму перегляду PDF-файлів і рендерую UIImageсторінку в окремому потоці (питання теж тут!) І представляю її, поки масштаб становить x1. CATiledLayerвізуалізація ударів, коли масштаб дорівнює> 1. iBooks використовує аналогічний подвійний підхід, як якщо ви прокручуєте сторінки, ви побачите нижчу версію сторінки лише менше секунди, перш ніж з'явиться чітка версія.

Я надаю по 2 сторінки на кожній стороні сторінки у фокусі, щоб PDF-зображення було готове замаскувати шар, перш ніж воно почне малювати. Сторінки знову знищуються, коли вони знаходяться на +2 сторінки від зосередженої сторінки.

Хтось має уявлення, незалежно від того, наскільки мало або очевидно, щоб покращити продуктивність / обробку пам'яті для малювання PDF-файлів? чи будь-які інші питання, що обговорюються тут?

EDIT: Деякі поради (Credit - Luke Mcneice, VdesmedT, Matt Matt Gallagher, Johann):

  • Збережіть будь-який носій на диску, коли зможете.

  • Використовуйте більші розміри tile, якщо візуалізація на TiledLayers

  • init часто використовуються масиви з об'єктами-заповнювачами, але інший підхід дизайну - це цей

  • Зауважте, що зображення відображатимуться швидше, ніж a CGPDFPageRef

  • Використовуйте NSOperationsабо GCD & Blocks для підготовки сторінок достроково.

  • зателефонуйте CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);раніше, CGContextDrawPDFPageщоб зменшити використання пам'яті під час малювання

  • init'ing your NSOperationsз docRef - це погана ідея (пам'ять), загортайте docRef в синглтон.

  • Скасовуйте непотрібність NSOperationsКоли ви зможете, особливо якщо вони будуть використовувати пам'ять, остерігайтеся залишати контексти відкритими!

  • Переробляйте об’єкти сторінки та знищуйте невикористані представлення даних

  • Закрийте будь-які відкриті контексти, як тільки вони вам не знадобляться

  • при отриманні попереджень пам'яті випустіть і перезавантажте DocRef і будь-які кеші сторінок

Інші функції PDF:

Документація

Приклад проектів

  • Apple / ZoomingPDF - масштабування, UIScrollView,CATiledLayer
  • VFR / читач - масштабування, пейджинг, UIScrollView,CATiledView
  • лоб / листя - підкачка з приємними переходами
  • / skim - все, що здається (зчитувач PDF / редактор для OSX)

коментуючи, щоб переглядати
письмові

+1 і дякую за додавання всієї цієї інформації, хотілося б, щоб я мав це під час розробки свого читача;) також дякую за те, що ви додали запитання щодо анотацій у форматі PDF (він також містить відповіді із зразком коду). кілька днів тому я відкрив це: stackoverflow.com/questions/4097044/pdf-search-on-the-iphone У вас є поради?
pt2ph8

Я все ще не висвітлював це, тому я не міг сказати нічого іншого, окрім як вказати вам на блог випадкових ідей: random-ideas.net/posts/42 Дякую за повідомлення, однак я намагаюся зібрати всі проблеми PDF в один місце.
Люк Макнейс

8
У моїй компанії ми використовували для візуалізації PDF, нотації тощо. Третє сторонне рішення називалося PSPDFKit, це не дешево, але варто: pspdfkit.com
János

1
+1 Я дотримувався цих корисних порад для мого перегляду PDF з відкритим кодом Swifty PDF github.com/prcela/SwiftyPDF
Prcela

Відповіді:


103

Я створив такий тип додатків, використовуючи приблизно той самий підхід, за винятком:

  • Я кешую створене зображення на диску і завжди генерую два-три зображення заздалегідь в окремому потоці.
  • Я не накладаю накладки, 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. Я синхронізую частину, до якої я отримую доступ до pdfDocресурсу, оскільки звільняю його і відтворюю її під час отримання пам'яті. Здається, що CGPDFDocumentRefоб’єкт робить якийсь внутрішній кешування, якого я не знайшов, як позбутися.


1
який ваш підхід, коли користувач починає масштабування?
Люк Макнейс

2
@Luke: Я змінив пост, щоб відповісти
VdesmedT

1
Дякую за це, і поради про кешування CGPDFDocumentRef.
Люк Макнейс

Просто швидке запитання: чому ви отримуєте pdfPageRef і перетворюєте, коли шкала дорівнює 1,0? тому що я бачу, що ви намалюєте baseImage (зображення від робочого елемента bg?) перед тим, як створити перетворення.
Люк Макнейс

@Luke: Опублікуйте тут будь-яке поліпшення продуктивності, яке ви могли б зробити, будь ласка. Дякую !
VdesmedT

67

Для простого та ефективного переглядача PDF, коли вам потрібен лише обмежений функціонал, тепер ви можете (iOS 4.0+) використовувати рамку QuickLook:

По-перше, вам потрібно зв’язати проти 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];

Так, це концептуально те саме, що використання UIWebView. Ми б не витрачали години, розгадуючи, як використовувати CGPDF * речі, якби це було так просто.
pt2ph8

5
Так, звісно. Я, звичайно, не представляю це як повне рішення. Але деякі читачі цього питання можуть не усвідомлювати, що для деяких цілей це розумне рішення. Оновіть відповідь, щоб зробити це зрозумілим.
Джошуа Дж. МакКіннон

6
+1 для цього. Моя основна зацікавленість - дозволити користувачеві переглядати деяку документацію в додатку, що знаходиться у форматі PDF. Мене не хвилює пошук, виділення чи будь-який інший дзвіночок - просто виконати PDF-рендерінг.
пам’ятки

2
Моя категорія UIImage + PDF - це швидкий PDF-рендерінг без втруднень із вбудованим шаром кешування. github.com/mindbrix/UIImage-PDF
Mindbrix

6

Оскільки iOS 11 , ви можете використовувати нативний фреймворк під назвою PDFKit для відображення та обробки PDF-файлів.

Після імпорту 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)
}

Детальніше про PDFKit читайте в документації Apple Developer.


-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);
     } }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.