Objective-CでPDFViewerを作成する 第3回

この記事は2014年9月28日に書かれたものです。内容が古い可能性がありますのでご注意ください。


Objective-CでPDFViewer 3回目

Objective-Cを使用してちょっとしたアプリケーションを作成してみようということでPDFViewerを作成しています。
今回は前回に続きページめくりの機能の部分を見ていきたいと思います。
前回はRootViewControllerについてみましたので今回はModelControllerを見ていこうと思います。

ModelControllerクラス

前回軽く説明したようにModelControllerでは表示させるページの管理を行っています。このクラス内で
表示を行う「DataViewController」を作成しています。
ではModelControllerを見てみましょう。

ModelController.hファイル

#import <UIKit/UIKit.h>

@class DataViewController;

@interface ModelController : NSObject <UIPageViewControllerDataSource>

- (DataViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard;
- (NSUInteger)indexOfViewController:(DataViewController *)viewController;

@end

ヘッダーファイルの「@interface」の部分を見るとプロトコルを取り込んでいます。
の部分です。プロトコルというのはある処理に必要なメソッドを定義したものと
なります。
今回使用するプロトコルではページがスクロールした呼び出されるメソッドが
定義されています。このプロトコルを取り込むことでこのクラスではページをめくったりスクロールする際の
メソッドが用意されたことになります。
ただしあくまで宣言しているだけなので実際ページがめくられた際の処理に関しては取り込んでいるクラスのほうで
実装する必要があります。
ページをめくる処理によって何を行うかは作成するアプリケーションにより異なりますのでプロトコルではあくまでページ送り
の際に呼ばれるメソッド名を定義だけしておき、あとは利用するクラス側でそのアプリケーションにあった処理を
実装することになります。

ヘッダーではそれ以外にも今のところ
指定されたPDFのページ番号からDataViewControllerを作成するメソッドと
現在表示しているページからページ番号を取得するメソッド
を使用するためそれを宣言しています。
次にクラスの実装ファイルのほうを見てみましょう。

ModelController.mファイル


#import "ModelController.h"
#import "DataViewController.h"

@interface ModelController()
@property (readonly, nonatomic) CGPDFDocumentRef pdf;

@end

@implementation ModelController

- (id)init
{
    self = [super init];
    if (self) {
        // PDFドキュメントを作成
        NSURL *url = [[NSBundle mainBundle] URLForResource:@"sample.pdf" withExtension:nil];
        _pdf = CGPDFDocumentCreateWithURL((CFURLRef)url);
    }
    return self;
}

 //指定されたPDFのページインデックスから新しいDataViewControllerを作成
- (DataViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard
{
    if(index > CGPDFDocumentGetNumberOfPages(_pdf) || index <= 0){
        return nil;
    }

    // 新しいDataViewControllerを作成
    DataViewController *dataViewController = [storyboard instantiateViewControllerWithIdentifier:@"DataViewController"];
    dataViewController.pdfPage = CGPDFDocumentGetPage(_pdf, index);
    dataViewController.maxPageCount = CGPDFDocumentGetNumberOfPages(_pdf);
    return dataViewController;
}

//引数で指定されたDataViewControllerで表示されているページからページ番号を取得
- (NSUInteger)indexOfViewController:(DataViewController *)viewController
{

    return CGPDFPageGetPageNumber(viewController.pdfPage);
}

#pragma mark - Page View Controller Data Source
//プロトコルで宣言されているメソッド
//ページが戻る際の処理
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{

    NSUInteger index = [self indexOfViewController:(DataViewController *)viewController];
    if(index == 1){
        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 == CGPDFDocumentGetNumberOfPages(self.pdf)){
        return nil;
    }
  index++;
    return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}

- (void)dealloc{
    if(_pdf != NULL){
        CGPDFDocumentRelease(_pdf);
    }
}

@end

次に各メソッド内の処理を見ていきましょう。

処理内容

オブジェクトの初期化処理の部分では表示するPDFドキュメントを管理するCGPDFDocumentRefを作成しています。
この辺りは第1回
で行っている処理をそのまま利用しています。
URLからCGPDFDocumentRefを作成しています。

次に「- (DataViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard」メソッドを使用して
指定されたPDFのページを表示するためのDataViewControllerを作成しています。
引数はindexに次に表示させるページの番号とストーリボードクラスのオブジェクトが入ってきます。
この関数内で行っている処理はUIStoryboardクラスの「instantiateViewControllerWithIdentifier」メソッドを使用して
新しくDataViewController作成しています。このメソッドの引数にはストーリボードのDataViewControllerのストーリボードID
を指定します。プロジェクト内のMain.storyboardファイルで確認できます。
そのあとはCGPDFDocumentRefから指定されたページを取得し新しく作成したDataViewControllerにそれを
渡すといったことを行っています。
また後々使用することになるためページの総数も取得しています。

「- (NSUInteger)indexOfViewController:(DataViewController *)viewController」メソッドでは現在表示されているPDFページから
ページインデックスを取得しています。
コアグラフィックの関数を使用することでページインデックスを取得できます。

その下にある2つのメソッドは先ほどヘッダーファイルで採用していたプロトコルで宣言されているメソッドです。
ヘッダーのところで説明したようにプロトコルはメソッドの宣言だけなのでクラス内で処理を実装することになります。
今回のPDFViewerではページが送られるたびに次のページを表示しなければならないのでそのための処理を
この部分に実装することになります。
「- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
                       viewControllerBeforeViewController:(UIViewController *)viewController」
こちらのメソッドではページが戻る際の処理を実装しています。
現在表示されいるページインデックスを取得して1つ前のページを表示させるDataViewControllerを作成しています。
当然ながら先頭ページの場合これ以上は前がないためあたらに作成することなく終了しています。
スタートのページは0ではなく1となっています。

「- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
                       viewControllerAfterViewController:(UIViewController *)viewController」
こちらのメソッドは先ほどとは逆でページを進む際の処理を実装しています。
現在表示されいるページインデックスを取得して1つ後ろのページを表示させるDataViewControllerを作成しています。
最後のページの場合これ以上は前がないためあたらに作成することなく終了しています。

ModelControllerではこうしたデータを表示させるためのDataViewControllerを作成したり管理する処理を
行っています。
次回は実際にデータを表示させるDataViewControllerを見ていきたいと思います。

  • このエントリーをはてなブックマークに追加

PAGE TOP