c-geru のすべての投稿

【Objective-C】アニメーションの終了イベント

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

とりあえずこんな感じで。delegateを設定して、終了時に呼ばれる関数をsetAnimationDidStopSelector:@selectorで指定する。

//アニメーション:設定
[UIView setAnimationsEnabled:YES];
[UIView beginAnimations:@"Flip" context:nil];
[UIView setAnimationDelegate : self ];
[UIView setAnimationDuration:0.5f];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:card cache:YES];
[UIView setAnimationDidStopSelector:@selector(compareCard)];

//アニメーション:開始
[UIView commitAnimations];

※card:アニメーションするUIView、compareCard:アニメーション終了時に呼ばれる関数

【参考】
あじもんすたー日記,iOSアニメーションでのミス

【Objective-C】再生リストからiCloudとDRM付きの曲を外す

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

前回書いたように、AVPlayer(AVQeuePlayer)でiPodライブラリの音楽を再生する場合、DRM付きの曲が再生できない。表示されているのに再生できないのはアプリとしてマズいので、再生リストから外す方法を考えてみました。

#import "SelectViewController.h"

@interface SelectViewController ()

@end

const int CATEGORY_ALBUM = 0;
const int CATEGORY_PLAYLIST = 1;
const int CATEGORY_ARTIST = 2;

@implementation SelectViewController
{
    NSDictionary *dataSource;
    NSMutableArray *key;

    int current_category;
}

//(中略)

-(void)setMusicCategory
{
    MPMediaQuery *query;
    switch (current_category) {
        case CATEGORY_ALBUM:
            query = [MPMediaQuery albumsQuery];
            break;
        case CATEGORY_PLAYLIST:
            query = [MPMediaQuery playlistsQuery];
            break;
        case CATEGORY_ARTIST:
            query = [MPMediaQuery artistsQuery];
            break;

        default:
            break;
    }

    //iCloudデータをフィルタリングで除外
    [query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:[NSNumber numberWithBool:NO] forProperty:MPMediaItemPropertyIsCloudItem]];

    NSArray *collectionList = query.collections;
    key = [[NSMutableArray alloc] init];
    NSMutableArray *albumList = [[NSMutableArray alloc] init];

    for (MPMediaItemCollection *collection in collectionList) {
        MPMediaItem *representativeItem = [collection representativeItem];
        NSString *albumtTitle = [representativeItem valueForProperty:MPMediaItemPropertyAlbumTitle];

        //DRMチェック(AVPlayerで再生できないため/DRM対象曲のみを弾く)
        int drm_count = 0;

        NSMutableArray *itemList = [[NSMutableArray alloc] init];

        for (MPMediaItem *item in collection.items) {
            NSURL *url_item = [item valueForProperty:MPMediaItemPropertyAssetURL];
            if (url_item == NULL) {
                drm_count++;
            } else {
                [itemList addObject:item];
            }
        }

        if (itemList.count > 0) {
            MPMediaItemCollection *collection_new = [MPMediaItemCollection collectionWithItems:itemList];

            [albumList addObject:collection_new];

            NSString *keyword;
            switch (current_category) {
                case CATEGORY_ALBUM:
                    keyword = albumtTitle;
                    break;
                case CATEGORY_PLAYLIST:
                    keyword = [collection valueForProperty:MPMediaPlaylistPropertyName];
                    break;
                case CATEGORY_ARTIST:
                    keyword = [representativeItem valueForProperty:MPMediaItemPropertyArtist];
                    break;

                default:
                    break;
            }

            [key addObject:[NSString stringWithFormat:@"%@",keyword]];
        }
	}

    dataSource = [NSDictionary dictionaryWithObjects:albumList forKeys:key];
}

※ SelectViewControllerは、UITableViewControllerを継承したカスタムクラスです。

上に書いた関数「setMusicCategory」は、UITableViewに選択させる再生リストを表示するためのdataSourceを作成するための関数で、ボタンなどによって再生リストの種類(プレイリスト/アルバム/アーティスト)を切り換えたときに・・・

current_category = CATEGORY_ALBUM;
[self setMusicCategory];
[self.myTableView reloadData];

※self.myTableViewは、UITableViewControllerに含まれるUITableView。

みたいな感じで使います。

・iCloudのデータを除外する
これはフィルタ使って弾けるので、上のソースのこの部分。

//iCloudデータをフィルタリングで除外
[query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:[NSNumber numberWithBool:NO] forProperty:MPMediaItemPropertyIsCloudItem]];

・DRM付きの曲を除外する
1つのアルバムで、DRMつきとDRMなしが混在することはないと思われるけど、プレイリストは任意に作れるので、引っ張ってきたMPMediaItemCollectionから、MPMediaItemのMPMediaItemPropertyAssetURLをひとつずつチェックしていくしかなさそうです。

for (MPMediaItem *item in collection.items) {
    NSURL *url_item = [item valueForProperty:MPMediaItemPropertyAssetURL];
    if (url_item == NULL) {
	drm_count++;
    } else {
	[itemList addObject:item];
    }
}

一応、コレで問題なさそうです。もっと効率的な方法があったら教えてください。

【Objective-C】AVQeuePlayerでMPMediaItemPropertyAssetURLがnullになる

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

AVPlayer(AVQeuePlayer)でiPodライブラリの音楽を再生するは、MPMediaItemからMPMediaItemPropertyAssetURLを取りだして、AVPlayerItemを生成する必要がある。

MPMediaItemPropertyAssetURL
A URL pointing to the media item, from which an AVAsset object (or other URL-based AV Foundation object) can be created, with any options as desired. Value is an NSURL object.

The URL has the custom scheme of ipod-library. For example, a URL might look like this:

ipod-library://item/item.m4a?id=12345
Usage of the URL outside of the AV Foundation framework is not supported.

Available in iOS 4.0 and later.

要はAV Foundation frameworkであるAVPlayer(AVQeuePlayer)で使える形に変換する必要があるってことですね。実際にはこんな感じ。

NSMutableArray *qItems;

for (MPMediaItem *mpItem in collection.items) {
	NSURL *url = [mpItem valueForProperty:MPMediaItemPropertyAssetURL];
	AVPlayerItem *api = [[AVPlayerItem alloc] initWithURL:url];
	[qItems addObject:api];
}

※collectionは、MPMediaItemCollection

ただこれでテストしていたら再生できない曲があって、いろいろ調べてみたら、MPMediaItemPropertyAssetURLがnullだった。

・昔、iTunesで購入したDRM付きの曲。
・iCloudにあって、iPhoneにダウンロードされていない曲。

この2つはURLがNULLになるらしい。

【参考】
blog.polikeiji.net: MPMediaItemPropertyAssetURLがnullのとき
iphone – How to detect if an MPMediaItem represents a DRM-protected audio track on iOS – Stack Overflow
ios4 – MPMediaItemPropertyAssetURL becomes null when Using MPMediaItems to play songs – Stack Overflow

で、こんな記事を見つけたので、再ダウンロードしてみたけど、DRMが外れない曲がある。まあ提供元が対応してなければ、そのままだよね。仮にDRMを外すことができたとしても、その行為をユーザにお願いするのはどうか?という問題もあるし。

iTunes Storeで購入したDRM楽曲は、再ダウンロードするとDRMフリーになる? | iPod love

ということで、この2種類は再生リストから外す方向で処理を考えてみます(続く)。

【Objective-C】AVPlayer関連リンク

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

とりあえずiPodライブラリを再生する音楽プレーヤー的なものを考えていたら、音の再生には3種類あるらしい。

・AVAudioPlayerクラス
・MPMusicPlayerControllerクラス
・AVPlayerクラス

【参考】Slow Days » Blog Archive » Core Audioざっくり覚え書き

今回考えているのは・・・

・バックグラウンド再生が可能(標準のミュージックプレイヤーと同じ)
・iPodライブラリ(自分がiPhoneに入れている音楽)の再生が可能

という前提なので、これだと「AVPlayerクラス」一択になるらしいので、関連リンクをメモしておきます。
(厳密には今回は、AVPlayerのサブクラスであるAVQueuePlayerを使っています)

Background Modes in iOS Tutorial
(これのPlaying Audioの箇所が役に立ちました)

かゆいところに手が届きそうなAVFoundationとMediaPlayerによる音楽再生アプリ作成メモ | Qiita
(ロック画面へアプリから情報表示など、知らなかったことばかり)

ios – Possible to insert item at top of queue using AVQueuePlayer? – Stack Overflow
(AVQueuePlayerで巻き戻しメソッドがないけどどうするの?キュー(Queue)なので、再生終わると配列から削除されます。で、原始的だけど、1つ前のAVPlayerItemを配列に入れて、順序を操作することで対応出来ます。ただ後ろに追加しかできないので、ちょっと面倒。)

AVPlayer Class Reference

AVQueuePlayer Class Reference

AVPlayerItem Class Reference

Objective-CのKVO(キー値監視)の使い方 | blog.nambo.jp

【2014.11.2 追記】
iOS の動画再生を試す | アカベコマイリ
(「AVPlayer の初期化」あたりは音楽でも同じ)

既にいくつか試してるのですが、その辺の記事は追々。

【Xcode】iTunes connectへのアプリ申請

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

昨日、無事アプリがリリースされました。パチパチ。「アイコン顔カメラ/icon face camera」というiPhoneアプリです。無料なので是非試してみて下さい。

アプリ申請でいくつか躓いたことがあったので、今後のためにメモ。
過去にもiOSアプリ制作に携わったことはあるのですが、リリース作業は今回が初めてでした。

・iTunes Connectは、iOS8リリースに合わせて更新された。
以前のと見た目が全然違っているので、ハッキリいって2014.8月以前の情報は無視した方がいいです。混乱します(笑)。

【参考】Xcode – 新しくなったiTunes Connectでのアプリの申請方法について – Qiita

・iAdはスクリーンショットに表示しない。
一週間経ってやっとレビューが始まったかと思ったら、数時間後にリジェクト(笑)。で、内容を確認してみると・・・

We found that your app and/or its metadata does not appear to include final content, which is not in compliance with the App Store Review Guidelines.

Specifically, your screenshots display empty iAd banners. It would be appropriate to remove the empty iAd banners from your screenshots.

で、訳すとこんな感じです。

私たちは、あなたのアプリケーションおよび/またはそのメタデータがApp Storeのレビューガイドラインを遵守していない最終的なコンテンツを含むように表示されないことがわかった。

具体的には、あなたのスクリーンショットは、空のiAdバナーを表示します。それはあなたのスクリーンショットから、空のiAdバナーを削除することが適切であろう。

iAd使ってるのにスクリーンショットに表示がないのはマズいと思って、わざわざ入れたのに完全に裏目でした。アプリ内の使っている画像もチェックして修正しました。

・アプリのバイナリのアップロードでエラー。
アーカイブを作って、Organizerからアップロードしたらエラーが出ました。

ERROR ITMS-9000: “Redundant Binary Upload. There already exists a binary upload with build version ‘1.0’ for train ‘1.0’”

調べてみたら、ビルド番号はアップロードごとに変更しないといけないらしいです。設定場所は、Xcodeの右カラムでプロジェクトを選んで、General>Identity>Buildです。デフォルトではVersionとBuildが同じになっていますが、Buildの番号を1.0.1などで変更すればいいらしいです。

【参考】ERROR ITMS-9000: “Redundant Binary Upload. There already exists a binary upload with build version ‘1.0’ for train ‘1.0’”のエラー対応方法

・iTunes connectでのビルド更新
新しくアップしたアプリ(バイナリ)は、マイAppのビルド欄に出てくるのかと思ってたら、マイAppのプレリリースタブの中に表示されるんですね。

・マイAppのビルド欄で「-」ボタン押して古いビルドを削除する。
・マイAppのビルド欄で「+」ボタン押して新しいビルドを選択する。

これで新しいビルドで再申請できました。

【参考】【アプリ申請】iTunes Connectが新しくなった!! 申請取り下げバイナリの再アップする方法

ちなみにiAdのスクリーンショット以外は問題(リジェクト)なかったのですが、申請からレビュー完了までかなり時間かかりました。iOS8のリリースと時期的に重なって、審査が混んでいたのかもしれませんが、参考までに書いておきます。

※申請時はリリース日付を2014.11.1に設定。尚、下記日時は米国時間です。

・September 28, 2014 22:34(c-geru)Prepare for Upload
・September 29, 2014 03:46(c-ger)Waiting For Review
・October 09, 2014 12:37(Apple)In Review
・October 09, 2014 19:09(Apple)Metadata Rejected >> iAdのスクリーンショットでリジェクト
・October 10, 2014 03:12(c-geru)Waiting For Review
・October 19, 2014 12:21(Apple)In Review
・October 19, 2014 13:02(Apple)Pending Developer Release >> レビュー完了
・October 20, 2014 09:25(c-geru)Processing for App Store >> リリース日付を10/21に変更
・October 20, 2014 09:31(Apple)Ready for Sale

【Xcode】iOS用のOCRライブラリ

あるんですね。有名なのは、この二つらしいです。

tesseract-ocr
nhocr

ググってみると、tesseract-ocrを使ったサンプルが多いようです。

iOSで日本語OCR!サンプルアプリ構築編〜iOS SDK 6.1 + tesseract-ocr 3.02〜 | Developers.IO
tesseract-orc で 自作プログラムに画像の文字を読ませてみた – Kikuchy’s Second Memory

一応、メモとして。

【Objective-C】MPMediaQueryで取得される曲情報にはiCloudも含まれる

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

MPMusicPlayerControllerでミュージックライブラリの再生をしていて、一覧に出てくるのに再生できない曲があって「何だろう?プログラムの問題?」といろいろ調べていたら・・・

MPMediaQueryで取得される曲情報にはiCloudも含まれる

ということらしい。リストにはあるけど、曲データがないので再生できない。というか、[MPMusicPlayerController play] ではエラーにはならずに再生した途端に「MPMusicPlayerControllerPlaybackStateDidChangeNotification」が返ってきて、MPMediaItem == NULL で再生終了になる。

※問題の出たアルバムは1曲しか含まれていなかったので。

具体的には、MPMediaItem に MPMediaItemPropertyIsCloudItem というプロパティがあります。これでiCloudにあるのかどうか判定できます。

・MPMediaItemPropertyIsCloudItem
A Boolean value indicating whether the media item is an iCloud item (YES), or not (NO). A media item is considered an iCloud item if it is available via iTunes Match and is not already stored on the device. Value is an NSNumber object representing a BOOL data type.

Available in iOS 6.0 and later.

・元々のコードはこんな感じ。

MPMediaQuery *query = [MPMediaQuery albumsQuery];

NSArray *albumlists = query.collections;

for (MPMediaItemCollection *albumlist in albumlists) {
MPMediaItem *representativeItem = [albumlist representativeItem];
NSString *albumtTitle = [representativeItem valueForProperty:MPMediaItemPropertyAlbumTitle];
}

・で、調べてみたら、iCloudのデータを除外するには [MPMediaQuery addFilterPredicate:] ですればOK。

MPMediaQuery *query = [MPMediaQuery albumsQuery];

[query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:[NSNumber numberWithBool:NO] forProperty:MPMediaItemPropertyIsCloudItem]];

NSArray *albumlists = query.collections;

for (MPMediaItemCollection *albumlist in albumlists) {
MPMediaItem *representativeItem = [albumlist representativeItem];
NSString *albumtTitle = [representativeItem valueForProperty:MPMediaItemPropertyAlbumTitle];
}

問題の出た曲データが特定できなくて、iOS純正のミュージックプレイヤーと自作アプリの曲アルバム一覧を比較していて、やっと気づきました。

【参考】
MPMediaItem Class Reference | iOS Developer Library
MPMediaQuary albumsQuery結果からiCloudのみにあるものを除く | 響雲
articles of samekard: iPod library access クラウド音源への対応
MPMediaQuery – ignore songs on Cloud (i.e., iTunes Match)

【Objective-C】音楽再生関連のまとめ

諸々調べているのでメモ。

MPMusicPlayerControllerクラス | Second Flush
MPMediaQuery Class Reference | iOS Developer Library — Pre-Release
iOSで、ミュージックライブラリにアクセスして音楽を再生する | nackpan Blog
音楽プレイヤのメソッド(1) | Second Flush
Objective-C – ARC環境下でAVAudioPlayerを使い柔軟かつシンプルにサウンドを再生する方法 – Qiita
プロフェッショナルプログラマー: iOS 音声再生(AVAudioPlayer)
MPMediaEntity Class Reference | iOS Developer Library
iOS – iTunesみたいに再生中の曲のアートワーク(ジャケット写真)に合わせて背景色と文字色を変える – Qiita
Media Player Framework Reference | iOS Developer Library
AddMusic | iOS Developer Library
AVPlayerDemo | iOS Developer Library
[iOS]AVPlayerを使う | nackpan Blog

【Objective-C】UIPageControlのページ切替タイミング

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.4】

前回の投稿絡みで、自前でUIPageControlを追加したときに、ページ数表示更新のタイミングでハマったのでメモ。

要は・・・

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed;
{
  ...
}

このページ切替アニメーションの終了タイミングで更新するのが吉、ということ。

自前のスクリプト載せようと思ったら、要らなくなったんで消した後でした・・・。

【参考】
これからiOS(iPhone)アプリを開発する人々へ: UIPageViewControllerについて

【Objective-C】UIPageViewControllerでUIPageControlが表示されない

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.4】

UIPageViewControllerを使った時に、下記の条件を満たせば自動的にUIPageControl(iPhoneのホーム画面にもある、現在位置を示す○)が表示されるらしい。

・UIPageViewControllerを生成するとき、nitWithTransitionStyle:UIPageViewControllerTransitionStyleScrollになっている。

    self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
                               navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
                               options:nil];

※self.pageViewControllerは、UIPageViewControllerです。

・ページが戻ったときに呼ばれるハンドラ

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    .....
{

・ページが進んだときに呼ばれるハンドラ

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    .....
{

【参考】
UIPageViewControllerの画面下部に表示されているPageControlを隠す – iOSアプリ開発の逆引き辞典
UIPageViewControllerの使い方 -Tips- – hyoromoのブログ

が、実際に自分で作ってみたら、何も表示されない。散々いろんなケースを疑い、果てはないなら自分でUIPageControlを実装してみたりして試行錯誤した結果、わかったことは・・・

「UIPageControlは表示されているが見えないだけ」

であったことが判明。

UIPageControlはざっくりいうと、

・親ViewにUIPageViewControllerを生成して、subViewとして追加。
・追加されたUIPageViewController.viewに、実際に表示する子Viewを入れ替えることで切替を表示する。

という仕組みになっている。

で、UIPageControlも親Viewに表示されるはずですが、親ViewのbackgroundColorが白(whiteColor)になっていたことが原因でした。
UIPageControlの初期値についての情報は得られなかったのですが、色々試した限りでは。。。

・[UIPageControl currentPageIndicatorTintColor](現在ページの○)>白
・[UIPageControl pageIndicatorTintColor](選択されてないページの○)>白+アルファ値(50%?)
・[UIPageControl backgroundColor](背景色)>白

となっているようです。よって親ViewのbackgroundColorが白だと何も見えない。ただ、あるはずの場所をタップすると動きます(見えないのでタップが可能ということ自体に気づいたのもだいぶ後になってからですが…)。

要するに色を替えて見えるようにしてやればいいけど、UIPageViewについてググっても、UIPageView+UIScrollViewをセットで使うときの話題ばかり。で、ようやく見つけたのがコレ。UIPageViewControllerの中にあるUIPageControlを参照すればいいようです。

    //ページコントロール(色を変更する)
    UIPageControl* proxy = [UIPageControl appearanceWhenContainedIn:[self.pageViewController class], nil];
    
//    NSLog(@"setPageIndicatorTintColor >> %@",[proxy pageIndicatorTintColor]);
//    NSLog(@"currentPageIndicatorTintColor >> %@",[proxy currentPageIndicatorTintColor]);
//    NSLog(@"backgroundColor >> %@",[proxy backgroundColor]);
    
    [proxy setPageIndicatorTintColor:[UIColor lightGrayColor]];
    [proxy setCurrentPageIndicatorTintColor:[UIColor blackColor]];
    [proxy setBackgroundColor:[UIColor whiteColor]];

※self.pageViewControllerは、UIPageViewControllerです。

ちなみにコメントになっているNSLogでデフォルトの色を確認しようとしたのだけれど、(null)しか返ってきませんでした。何でだろう?

【参考】
storyboard – iOS Page based template, regarding UIPageViewController and UIPageControl color of the dots – Stack Overflow

これでようやくUIPageControlが表示されました。左右スクロールによる表示制御やタップしたときの挙動は、デフォルトで設定されるようで、UIPageControlについて書いたコードは、上の色替えの部分のみです。

更に、デフォルトだとUIPageControlの位置がかなり下になるので、UIPageViewController.viewの高さを削ってやると、もうちょっと上の位置になります。

- (void)viewDidLoad
{
    ....
    // Change the size of page view controller
    self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 30);
    ....
{

最後に一応、コードを載せておきます。
ここでは親クラス(HelpViewController)と子クラス(DetailViewController)を使っています。
子クラス(DetailViewController)には・・・

@property NSUInteger pageIndex;
@property NSString *titleText;
@property NSString *captionText;
@property NSString *imageFile;

のプロパティがあって、生成時に表示する情報を渡しています。

・HelpViewController.h


#import <UIKit/UIKit.h>
#import "DetailViewController.h"

@interface HelpViewController : UIViewController<UIPageViewControllerDataSource,UIPageViewControllerDelegate>

@property (strong, nonatomic) UIPageViewController *pageViewController;
@property (strong, nonatomic) NSArray *pageTitles;
@property (strong, nonatomic) NSArray *pageImages;

@end

・HelpViewController.m

#import "HelpViewController.h"

@interface HelpViewController ()
{
    NSMutableArray *captionArray;
    NSMutableArray *imageArray;
    NSInteger currentPageIndex;
}

@end

@implementation HelpViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    //----
    
    //タイトル設定
    
    self.navigationItem.title = NSLocalizedString(@"conf_use", NULL);
    
    // テキストを設定する
    captionArray = [[NSMutableArray alloc] init];
    [captionArray addObject:NSLocalizedString(@"use_caption1", NULL)];
    [captionArray addObject:NSLocalizedString(@"use_caption3", NULL)];
    [captionArray addObject:NSLocalizedString(@"use_caption4", NULL)];
    [captionArray addObject:NSLocalizedString(@"use_caption5", NULL)];
    [captionArray addObject:NSLocalizedString(@"use_caption6", NULL)];
    [captionArray addObject:NSLocalizedString(@"use_caption7", NULL)];
    [captionArray addObject:NSLocalizedString(@"use_caption8", NULL)];
    [captionArray addObject:NSLocalizedString(@"use_caption9", NULL)];
    
    //
    // 画像ファイル名名を設定する
    imageArray = [[NSMutableArray alloc] init];
    [imageArray addObject:NSLocalizedString(@"help_img01", NULL)];
    [imageArray addObject:NSLocalizedString(@"help_img03", NULL)];
    [imageArray addObject:NSLocalizedString(@"help_img04", NULL)];
    [imageArray addObject:NSLocalizedString(@"help_img05", NULL)];
    [imageArray addObject:NSLocalizedString(@"help_img06", NULL)];
    [imageArray addObject:NSLocalizedString(@"help_img07", NULL)];
    [imageArray addObject:NSLocalizedString(@"help_img08", NULL)];
    [imageArray addObject:NSLocalizedString(@"help_img09", NULL)];

    // UIPageViewControllerを生成
    self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
                               navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
                               options:nil];
    
    self.pageViewController.dataSource = self;
    self.pageViewController.delegate = self;
    
    DetailViewController *startingViewController = [self viewControllerAtIndex:0];
    NSArray *viewControllers = @[startingViewController];
    [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
    
    // UIPageViewControllerのサイズを変更(UIPageControlを上に表示する為)
    self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 30);
    
    [self addChildViewController:_pageViewController];
    [self.view addSubview:_pageViewController.view];
    [self.pageViewController didMoveToParentViewController:self];
    
    //ページコントロール(色を変更する)
    UIPageControl* proxy = [UIPageControl appearanceWhenContainedIn:[self.pageViewController class], nil];
    
    [proxy setPageIndicatorTintColor:[UIColor lightGrayColor]];
    [proxy setCurrentPageIndicatorTintColor:[UIColor blackColor]];
    [proxy setBackgroundColor:[UIColor whiteColor]];
    
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (DetailViewController *)viewControllerAtIndex:(NSUInteger)index
{
    if (([captionArray count] == 0) || (index >= [captionArray count])) {
        return nil;
    }
    
    // 新しいページのviewを生成
    DetailViewController *DetailViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"DetailViewController"];
    DetailViewController.imageFile = imageArray[index];
    DetailViewController.captionText = captionArray[index];
    DetailViewController.titleText = NSLocalizedString(@"conf_use", NULL);

    DetailViewController.pageIndex = index;
    
    return DetailViewController;
}

#pragma mark - Page View Controller Data Source

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    NSUInteger index = ((DetailViewController*) viewController).pageIndex;
    
    if ((index == 0) || (index == NSNotFound)) {
        return nil;
    }
    
    index--;

    return [self viewControllerAtIndex:index];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    NSUInteger index = ((DetailViewController*) viewController).pageIndex;
    
    if ((index >= [captionArray count]) || (index == NSNotFound)) {
        return nil;
    }
    
    index++;
    if (index == [captionArray count]) {
        return nil;
    }
    
    return [self viewControllerAtIndex:index];
}

- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
    return [captionArray count];
}

- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
    return 0;
}

-(void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
    if(completed){
        DetailViewController *vc =[self.pageViewController.viewControllers lastObject];
        currentPageIndex =  vc.pageIndex;
    }
}


@end

・DetailViewController.h

#import <UIKit/UIKit.h>

@interface DetailViewController : UIViewController
    @property (weak, nonatomic) IBOutlet UITextView *caption;
    @property (weak, nonatomic) IBOutlet UIImageView *helpImage;

@property NSUInteger pageIndex;
@property NSString *titleText;
@property NSString *captionText;
@property NSString *imageFile;

@end

・DetailViewController.m

#import "DetailViewController.h"

@interface DetailViewController ()

@end

@implementation DetailViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    _helpImage.image = [UIImage imageNamed:self.imageFile];
    self.title = self.titleText;
    _caption.text = self.captionText;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

あまりに単純な話ですが、同じような目に遭う人もいるかもしれないのでメモとして。