トップ «前の日記(2012-04-10) 最新 次の日記(2012-04-16)» 編集

Cocoa練習帳

iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど

2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|

2012-04-14 [iOS]電子書籍への道:PDF表示(その3)

前回のサンプル・プログラムをリソースとして持っているPDF文書からページ数を取得して、頁に対応したページを表示するように拡張する。

Page-Based Applicationの雛形は、データを管理するクラスModelControllerが用意されている。また、iOSには複数の文書を扱う仕組みが用意されているので、いずれは、それへの対応という事になるが、今回は自分が単一文書を扱う場合に用意している独自のDocumentクラスでPDF書類を管理する事にした。

本当は、PDF文書の扱いはDocumentクラスで抽象化すべきと思うが、サンプル・コードという事で、単にプロパティとして持っている事で許して欲しい。

@interface Document : NSObject
	…
@property (nonatomic, assign) CGPDFDocumentRef  pdf;
	…
@end

そして、各コントローラクラスで、Documentクラスをプロパティとして参照するようにする。例えば、初期化をinitメソッドで行う場合は、以下のとおり。

#import "AppDelegate.h"
	…
- (id)init
{
    self = [super init];
    if (self) {
        AppDelegate	*appl = nil;
        appl = (AppDelegate *)[[UIApplication sharedApplication] delegate];
        self.document = appl.document;
    }
    return self;
}

ページ・ビュー・コントローラの利用では、各クラスの関係や、モデルの初期化のタイミングは少し悩んだので、分かりやすいように掻い摘んで説明する。

ADCのサンプルZoomingPDFViewでは、決めうちで、リソースで持っているPDF文書の1頁だけを表示するに固定されているので、PDFScrollViewの初期時にPDF文書関連の初期化を行っていたが、それを止めて、外部から指定されたタイミングで指定された頁を表示する様に変更する。

@interface PDFScrollView : UIScrollView 
	…
- (void)setIndexOfPDF:(NSUInteger)index;
- (NSUInteger)getIndexOfPDF;
	…
@end
 
@implementation PDFScrollView
	…
- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
    }
    return self;
}
 
- (void)awakeFromNib
{
    self.decelerationRate = UIScrollViewDecelerationRateFast;
    self.delegate = self;
 
    AppDelegate	*appl = nil;
    appl = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    self.document = appl.document;
}
 
- (void)setIndexOfPDF:(NSUInteger)index
{
    self.page = CGPDFDocumentGetPage(self.document.pdf, index + 1);
    CGPDFPageRetain(self.page);
    
    CGRect pageRect = CGPDFPageGetBoxRect(self.page, kCGPDFMediaBox);
    self.pdfScale = self.frame.size.width/pageRect.size.width;
    pageRect.size = CGSizeMake(pageRect.size.width * self.pdfScale, pageRect.size.height * self.pdfScale);
    
    UIGraphicsBeginImageContext(pageRect.size);
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
    CGContextFillRect(context,pageRect);
    
    CGContextSaveGState(context);
    CGContextTranslateCTM(context, 0.0, pageRect.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
     
    CGContextScaleCTM(context, self.pdfScale, self.pdfScale);	
    CGContextDrawPDFPage(context, self.page);
    CGContextRestoreGState(context);
    
    UIImage *backgroundImage = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    
    self.backgroundImageView = [[UIImageView alloc] initWithImage:backgroundImage];
    self.backgroundImageView.frame = pageRect;
    self.backgroundImageView.contentMode = UIViewContentModeScaleAspectFit;
    [self addSubview:self.backgroundImageView];
    [self sendSubviewToBack:self.backgroundImageView];
    
    self.pdfView = [[TiledPDFView alloc] initWithFrame:pageRect andScale:self.pdfScale];
    [self.pdfView setPage:self.page];
    
    [self addSubview:self.pdfView];
}
 
- (NSUInteger)getIndexOfPDF
{
    return (NSUInteger)CGPDFPageGetPageNumber(self.page);
}
…
@end

雛形ではカレンダーのデータを保持していたDataViewControllerクラスを表示している頁番号とPDFScrollViewクラスを扱うように変更する。

@interface DataViewController : UIViewController
	…
@property (strong, nonatomic) Document                  *document;
@property (nonatomic, assign) NSUInteger                index;
	…
- (void)setIndexOfPDF:(NSUInteger)index;
- (NSUInteger)getIndexOfPDF;
@end
 
@implementation DataViewController
	…
- (void)setIndexOfPDF:(NSUInteger)index
{
    self.index = index;
    PDFScrollView   *pdfScrollView = (PDFScrollView *)self.view;
    [pdfScrollView setIndexOfPDF:index];
}
 
- (NSUInteger)getIndexOfPDF
{
    return self.index;
}
	…
@end

頁に対応するビュー・コントローラDataViewControllerは、モデル・コントーラで管理される?ということで、PDF表示に対応させる。

@implementation ModelController
	…
- (DataViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard
{   
    if (CGPDFDocumentGetNumberOfPages(self.document.pdf) < index)
        return nil;
     
    DataViewController *dataViewController = [storyboard instantiateViewControllerWithIdentifier:@"DataViewController"];
    [dataViewController setIndexOfPDF:index];
    return dataViewController;
}
 
- (NSUInteger)indexOfViewController:(DataViewController *)viewController
{   
    return viewController.index;
}
 
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    NSUInteger index = [self indexOfViewController:(DataViewController *)viewController];
    if ((index == 0) || (index == NSNotFound)) {
        return nil;
    }
     
    index--;
    return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}
 
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    NSUInteger index = [self indexOfViewController:(DataViewController *)viewController];
    if (index == NSNotFound) {
        return nil;
    }
    
    index++;
    if (index == CGPDFDocumentGetNumberOfPages(self.document.pdf))
        return nil;
    return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}
	…
@end

このサンプルをGitHubから取得して、実行すると、強制的に著者がCocoa勉強会で発表した文書を読まされる事になる。

Books

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/Books - GitHub

_ 関連情報

第4回 出版プラットフォームとしてのiOS
Think IT 連載: iOSでつくる2011アプリ開発状況から。
Quartz 2d Programming Guide
Acroll View Programming Guide for iOS
ZoomingPEFViewer
Apple Developerサイトの情報。
Programming with Quartz: 2D and PDF Graphics in Mac OS X
OS XとiOSのCore Graphicsの解説書。

トップ «前の日記(2012-04-10) 最新 次の日記(2012-04-16)» 編集