Fast and Lean PDF Viewer для iPhone / iPad / iOs - советы и подсказки?


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

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

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

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

UIImage Метод:

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

Метод CATiledLayer:

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

как правило, PDF-зрители тоже довольно тяжелы по памяти. Даже контролировать использование памяти пример зумирования от Apple в формате PDF.

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

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

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

EDIT: Некоторые Советы (Кредит-Люк Макнейс, Вдесмедт, Мэтт Галлахер, Иоганн):

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

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

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

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

  • использовать NSOperations или GCD & блоки для подготовки страниц вперед времени.

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

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

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

  • утилизация объектов страницы и уничтожение неиспользуемых представлений

  • закройте все открытые контексты, как только они вам не нужны

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

другие функции PDF:

  • получение ссылок внутри PDF (а здесь и здесь)

    • понимание PDF Rect для позиционирования ссылок

    • преобразование PDF annot datestrings

    • получение цели ссылки (получение номера страницы из /Dest массив)

  • получение оглавления

  • заголовок документа и ключевые слова

  • получение необработанного текста (и здесь, и здесь, и здесь (позиционирование сфокусировано))

  • Поиск(и здесь) (не работает со всеми PDF-файлами (некоторые просто показывают странные символы, я думаю, что это проблема с кодировкой, но я не уверен) - кредит BrainFeeder)

  • CALayer и закадровый рендеринг-рендеринг следующей страницы для быстрого / плавного отображения

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

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

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

4 ответа:

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

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

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

во-первых, вам нужно связать против 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, вы можете использовать собственный фреймворк под названием 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.

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