Быстрый и быстрый PDF Viewer для iPhone/iPad/iOs - советы и подсказки?

В последнее время появилось много вопросов о создании PDF файлов.

Да, вы можете очень легко визуализировать PDF с помощью UIWebView, но это не дает производительности и функциональности, которые вы ожидаете от хорошего просмотра PDF.

Вы можете нарисовать страницу PDF в CALayer или в UIImage. У Apple даже есть пример кода, чтобы показать, как рисовать большой PDF в Zoomable UIScrollview

Но те же проблемы продолжают возникать.

Метод UIImage:

  • PDF в UIImage не оптически масштаб, а также подход уровня.
  • ЦП и память попали в UIImages из a PDFcontext ограничивает/препятствует его использованию для создания рендеринг в реальном времени новых уровней масштабирования.

Метод CATiledLayer:

  1. Theres существенные накладные расходы (время) рисование полной страницы PDF на CALayer: отдельные фрагменты можно увидеть рендерингом (даже с настройкой tileSize)
  2. CALayers не может быть подготовлен раньше   время (отображается вне экрана).

Как правило, зрители в формате PDF слишком тяжелы в памяти. Даже отслеживайте использование памяти с помощью масштабируемого PDF файла.

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

Im рендерит 2 страницы с каждой стороны страницы в фокусе, чтобы изображение в формате PDF было готово замаскировать слой до начала рисования. Страницы снова уничтожаются, когда они находятся на +2 страницах от сфокусированной страницы.

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

РЕДАКТИРОВАТЬ: Некоторые советы (Credit-Luke Mcneice, VdesmedT, Matt Gallagher, Johann):

  • Сохраните любой носитель на диске, когда сможете.

  • Используйте большие размеры tileSizes, если рендеринг на TiledLayers

  • init часто используются массивы с объектами-заполнителями, альтернативно другой подход к разработке этот

  • Обратите внимание, что изображения будут отображаться быстрее, чем CGPDFPageRef

  • Используйте NSOperations или GCD и Blocks, чтобы подготовить страницы вперед времени.

  • вызывать CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault); до CGContextDrawPDFPage, чтобы уменьшить использование памяти при рисовании

  • init'ing вашего NSOperations с docRef - плохая идея (память), оберните docRef в одноэлементный.

  • Отменить needless NSOperations Если вы можете, особенно если они будут использовать память, остерегайтесь оставлять контексты открытыми, хотя!

  • Утилизировать страницы страниц и уничтожить неиспользуемые представления

  • Закройте все открытые контексты, как только они вам не понадобятся.

  • при получении предупреждений памяти отпустите и перезагрузите DocRef и любую страницу Cache

Другие возможности PDF:

Documentation

Примеры проектов

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

Ответы

Ответ 1

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

  • Я кэширую сгенерированное изображение на диске и всегда генерирую два-три изображения заранее в отдельном потоке.
  • Я не накладываюсь на 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 выполняет некоторое внутреннее кэширование, которое я не нашел, как избавиться.

Ответ 2

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

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

Вам нужно связать с QuickLook.framework и #import <QuickLook/QuickLook.h>

Ответ 3

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