Не секрет, что PDF документ в приложении можно показать в UIWebView. Но, что делать, если вас не устраивает то, как UIWebView отображает документ? Может быть вы хотите сделать свой механизм перехода по страницам?

В iOS PDF можно отобразить через CoreGraphics. При использовании этого подхода каждая страница рисуется отдельно. Т.е. если нужно сделать переход по страницам в документе — нужно будет все делать вручную.

Итак, предположим, что у нас в Bundle приложения лежит PDF-файл idevby.pdf. Как его отобразить? Создадим класс PDFView, который будет это отображать:

@interface PDFView : UIView
@end

В классе перекроем метод drawRect::

- (void)drawRect:(CGRect)rect
{
    // путь до документа
    CFURLRef url = (CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"idevby" ofType:@"pdf"]];

// открываем документ CGPDFDocumentRef pdfDocument = CGPDFDocumentCreateWithURL(url);

// если в документе нет страниц - ничего отображать не нужно if ( CGPDFDocumentGetNumberOfPages(pdfDocument) > 0 ) { // получим первую страницу (страницы нумеруются с первой) CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdfDocument, 1);

// получим необходимое преобразование CGAffineTransform t = CGPDFPageGetDrawingTransform(pdfPage, kCGPDFCropBox, self.bounds, 0, 1);

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSaveGState(context);

// применим преобразование CGContextTranslateCTM(context, 0, self.bounds.size.height); CGContextScaleCTM(context, 1, -1); CGContextConcatCTM(context, t);

// отрисуем страницу CGContextDrawPDFPage(context, pdfPage);

CGContextRestoreGState(context); }

// закроем документ CGPDFDocumentRelease(pdfDocument); }

Рассмотрим основные моменты.

При отрисовке PDF-страницы мы сохраняем состояние контекста, а затем восстанавливаем его:

CGContextSaveGState(context);
...
CGContextRestoreGState(context);

Этого можно было бы и не делать, но если вы захотите рисовать что-то после отрисовки PDF-страницы — это необходимо, иначе то, что вы нарисуете может быть искривлено.

Нам необходимо отразить контекст по вертикали, иначе документ будет показан «вверх ногами». Это связано с тем, что в данном случае ось Y направлена снизу вверх.

CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1, -1);

Ну и самое интересное:

CGAffineTransform t = CGPDFPageGetDrawingTransform(pdfPage, kCGPDFCropBox, self.bounds, 0, 1);
...
CGContextConcatCTM(context, t);

CGContextDrawPDFPage(context, pdfPage);

Функция CGPDFPageGetDrawingTransform возвращает трансформацию для контекста, которую нужно применить, чтобы целиком отобразить заданную область страницы в заданной области view. Трансформацию применяет функция CGContextConcatCTM. Первым аргументом функции CGPDFPageGetDrawingTransform является страница, которую хотим отобразить. Второй аргумент — область документа для отображения. Области определены так:

enum CGPDFBox {
kCGPDFMediaBox = 0,
kCGPDFCropBox = 1,
kCGPDFBleedBox = 2,
kCGPDFTrimBox = 3,
kCGPDFArtBox = 4
};

Третий аргумент — область на view, где будет отображена страница, в данном случае мы используем всю область. Четвертый аргумент — угол вращения в градусах, должен быть обязательно кратным 90. Пятый аргумент — сохранять ли пропорции сторон при отрисовке.

Конечно-же, этот способ более трудоемкий, чем способ с использованием UIWebView. Но, используя такой подход, мы можем получить больше возможностей для кастомизации отображения PDF. Есть и минус данного подхода — если в документе есть ссылки — они не будут активными и необходимо будет применять дополнительные средства.

На этом все, спасибо за внимание. Загрузить исходный код можно отсюда.

Напоследок несколько примеров:

PDF

CGPDFPageGetDrawingTransform(pdfPage, kCGPDFCropBox, self.bounds, 0, 1)

PDF

CGPDFPageGetDrawingTransform(pdfPage, kCGPDFCropBox, self.bounds, 90, 1)

PDF

CGPDFPageGetDrawingTransform(pdfPage, kCGPDFCropBox, self.bounds, 90, 0)

PDF

CGPDFPageGetDrawingTransform(pdfPage, kCGPDFCropBox, CGRectInset(self.bounds, 50, 100), 0, 1)