UISplitViewControllerのマスタービュー表示/非表示を、ボタンタップではなくプログラムから切り替える方法についてです。
UISplitViewControllerクラスは、-splitViewController:shouldHideViewController:inOrientation:デリゲートメソッドか、デリゲートメソッドに渡されるUIBarButtonItemオブジェクトとを使うことで、Masterビュー(左側に表示されるビュー)の表示/非表示を切り替えられます。たとえば次のようにデリゲートメソッドを実装していると、縦方向の表示のときはMasterビューを隠して画面全体でDetailビュー(右側に表示)を表示できます。
/* orientationで示す方向で、マスタービューを隠すかどうか
* UISplitViewControllerのビュー表示時、あるいはデバイス方向を変えたときに呼ばれる
*/
- (BOOL)splitViewController:(UISplitViewController *)svc // ← UISplitViewController
shouldHideViewController:(UIViewController *)vc // ← マスタービューのビューコントローラ
inOrientation:(UIInterfaceOrientation)orientation // ← 方向(UISplitViewController)
{
// 縦方向のときは隠す。横方向では表示
return UIInterfaceOrientationIsPortrait(orientation);
}
また、別の-splitViewController:willHideViewController:withBarButtonItem:forPopoverController:デリゲートメソッドで渡されるUIBarButtonItemには、UISplitViewControllerオブジェクトとそのメソッドが、それぞれtargetとactionプロパティにセットされています。そのため、ツールバーやナビゲーションバーにセットしておけば、ユーザーの操作でマスタービューを自由に出したり消したりできます。
- (void)splitViewController:(UISplitViewController *)splitController
willHideViewController:(UIViewController *)viewController
withBarButtonItem:(UIBarButtonItem *)barButtonItem
forPopoverController:(UIPopoverController *)popoverController
{
NSLog(@"action:[%@] target:[%@]", NSStringFromSelector(barButtonItem.action), barButtonItem.target);
// → action:[toggleMasterVisible:] target:[<UISplitViewController: 0x…>]
}
ところがここに問題があり、マスタービューの表示を切り替える手段がUIBarButtonItemでしか提供されず、その中で呼んでいるAPIがプライベートAPIなので、プログラムから直接呼ぶことができないのです。
「ある項目を選択したときだけ画面を広くして使いたい」「画面上のツールバーやナビゲーションバー以外のUIでボタンを設置したい」というユースケースはよくありますが、これではそんな要望には応えられません。どうしてこんな設計にしたか分かりませんが、とにかく無理なものは無理です。
ではどうしたらよいか。プライベートAPIを明示的に呼ばず安全に表示/非表示を切り替える方法は、実は簡単な方法があります。次に示すように、barButtonItemのtargetとactionプロパティの情報をそのまま使って、メッセージを送信するだけです。
// UISplitViewControllerにMasterビューの表示切り替えを要求する
// ※ barButtonItemはデリゲートメソッドで届いたときに保持しておく
[barButtonItem.target performSelector:barButtonItem.action];
こうすることで、UISplitViewControllerの内部APIに触れる・知ることなく目的を達成できます。任意のUIやジェスチャでも、ビューの表示を切り替えられますね。
@ikesyo さん、Twitterで困っていたときにコメントしていただき、ありがとうございました。