UIPopoverControllerチュートリアル その1
スポンサーリンク
UIPopoverControllerはiPadアプリではよく使われるものですが、結構あつかいにくいものだと思います。考えてプログラミングしないとメモリリークにつながります。まずは簡単なサンプルから説明して、問題点とその解決方法を説明していきたいと思います。
Popoverを表示する
以下のサンプルをダウンロードして下さい。
https://github.com/stack3/STPopoverControllerSample
起動してPopover Sampleを選択。Popoverボタンを押すとPopoverが表示されます。
Popoverの外側、もしくはPopoverのCloseボタンを押すとPopoverが閉じます。
プログラムを見てみましょう。STPopoverSampleViewController.mのdidTapPopoverButtonでPopoverボタンを押した時のイベントを処理します。
STPopoverSampleViewController.m
- (void)didTapPopoverButton { STPopoverSampleContentViewController *contentViewController = [[STPopoverSampleContentViewController alloc] init]; contentViewController.delegate = self; _popoverController = [[UIPopoverController alloc] initWithContentViewController:contentViewController]; _popoverController.delegate = self; [_popoverController presentPopoverFromRect:_popoverButton.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; }
処理の流れは以下のとおり
- STPopoverSampleContentViewControllerがPopoverの中身に表示されるUIViewController
- UIPopoverController#initWithContentViewControllerにそれを渡します
- UIPopoverController#presentPopoverFromRect:inView:permittedArrowDirections:animatedでPopoverを表示します
注意すべきなのはUIPopoverControllerのオブジェクトをメンバ変数で保持する必要があることです。ローカル変数ですとメソッドのスコープを抜けたらオブジェクトが解放されてしまいPopoverが表示されません。
このオブジェクトを保持しないといけないところがメモリリークに繋がる原因になるのですが・・・
Popoverのサイズについて
Popoverのサイズは中身となるUIViewController(UIPopoverController#contentViewController)のcontentSizeInPopoverプロパティで決まります。今回の場合は、STPopoverSampleContentViewControllerのinitメソッドで設定しています。
self.contentSizeForViewInPopover = CGSizeMake(320, 280);
ここで設定した値がPopoverの中身のサイズになります。Popoverの枠を含めたサイズではありません。
ちなみにUIPopoverControllerにもPopoverの中身のサイズを指定するプロパティ(popoverContentSize)があります。しかし、こちらは基本的には使わないほうが良さそうです。UINavigationControllerが中身になるとき適切なサイズにならないことがあります。また中身となるUIViewController自身がcontentSizeInPopoverによって適切なサイズを決定する方が自然なように思います。
UIPopoverControllerオブジェクトの解放
Popoverが非表示になった時、UIPopoverControllerを解放しないといけません。このサンプルの場合は、STPopoverSampleViewControllerのメンバ変数_popoverControllerをnilにする必要があります。
先ほど説明したようにPopoverが非表示になるタイミングは2通りあります。
- Popoverの外側をタップした場合
- PopoverのCloseボタンをタップした場合
Popoverの外側をタップして非表示になる場合
この場合は、UIPopoverControllerDelegateによって判断出来ます。よってPopoverを表示するViewControllerで、このDelegateを実装する必要があります。
STPopoverSampleViewController.h
@interface STPopoverSampleViewController : UIViewController<UIPopoverControllerDelegate, STPopoverContentViewControllerDelegate>
非表示になるタイミングでUIPopoverController#popoverControllerDidDismissPopover:が呼ばれるので、ここで_popoverControllerをnilにして解放します。
STPopoverSampleViewController.m
#pragma mark - UIPopoverControllerDelegate - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController { _popoverController = nil; NSLog(@"PopoverSampleViewController released popoverController."); }
PopoverのCloseボタンをタップして非表示にする場合
この場合は、プログラムでPopoverを非表示にする必要があります。これはUIPopoverController#dismissPopoverAnimated:を呼ぶことで実現できます。やっかいなのが、Popoverの中身であるUIViewControllerからUIPopoverControllerを参照する仕組みがないことです。よってdismissPopoverAnimatedを呼び出すためには工夫が必要です。
- 中身となるUIViewControllerが独自にDelegateを宣言
- Closeボタンが押されたらDelegateメソッドで通知する
- Popoverを開いたUIViewControllerは、上記Delegateメソッドを実装
- その中で、メンバー変数として保持しているUIPopoverControllerのdismissPopoverAnimatedを呼ぶ。
今回はこの方法で実装したものを説明します。先に言っておくと、良い方法だとは思いませんが、問題を示すためにまず説明します。
STPopoverSampleContentViewController.h
@interface STPopoverSampleContentViewController : UIViewController @property (weak, nonatomic) iddelegate; @end @protocol STPopoverContentViewControllerDelegate - (void)popoverContentViewControllerDidTapCloseButton:(STPopoverSampleContentViewController *)sender; @end
Closeボタンを押した時にDelegateメソッドを呼びます。
STPopoverSampleContentViewController.m
- (void)didTapCloseButton { [_delegate popoverContentViewControllerDidTapCloseButton:self]; }
次にSTPopoverSampleViewControllerで、このDelegateメソッドを実装。dismissPopoverAnimatedを呼び出し、_popoverControllerをnilにします。ここでnilにしても非表示のアニメは行われます。
STPopoverSampleViewController.m
#pragma mark - STPopoverContentViewControllerDelegate - (void)popoverContentViewControllerDidTapCloseButton:(id)sender { if (_popoverController) { [_popoverController dismissPopoverAnimated:YES]; _popoverController = nil; NSLog(@"PopoverSampleViewController released popoverController."); } }
これで然るべきタイミングでPopoverが非表示され、UIPopoverControllerオブジェクトも解放されます。しかし、さまざまな箇所でPopoverを表示したい時、随所でこの処理を書かなければいけないとなると大変です。
次回は、その辺の解決方法について説明したいと思います。
この記事をシェア
スポンサーリンク