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

В последнее время было много вопросов о рисовании PDF.

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

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

Но все еще возникают проблемы.

UIImage Метод:

  1. PDF-файлы в UIImage не масштабируются оптически, как и подход Layer.
  2. Процессор и память ударили при генерации UIImages из PDFcontextограничивает / запрещает использовать его для создания в реальном времени новых уровней масштабирования.

CATiledLayer Метод:

Существуют значительные накладные расходы (время) при рисовании полной страницы PDF на CALayer: можно увидеть отдельные плитки, визуализируемые (даже с настройкой tileSize) CALayers не может быть подготовлено заранее (отрисовка за кадром).

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

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

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

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

РЕДАКТИРОВАТЬ: Некоторые советы (Кредит-Люк Макнейс, VdesmedT, Мэтт Галлахер, Иоганн):

  • Сохраняйте любые носители на диск, когда это возможно.

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

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

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

  • Используйте NSOperations или GCD & Blocks для предварительной подготовки страниц.

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

  • инициализация вашего NSOperations с помощью docRef - плохая идея (память), заверните docRef в одиночный файл.

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

  • Перерабатывайте объекты страницы и уничтожайте unused views

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

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

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

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

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

Ответы [ 4 ]

103 голосов
/ 08 октября 2010

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

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

66 голосов
/ 22 марта 2011

Для простого и эффективного средства просмотра 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];
6 голосов
/ 29 января 2018

Поскольку 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.

0 голосов
/ 18 июня 2013
 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);
     } }
...