【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

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