Master-Detail template で tabBarController を追加する方法

久しぶりにはまりました。Master-Detail template はよく出来ていて、手を入れる必要がある個所が分かってくれば凄く便利なんです。

でも、TabBar を追加しようとすると状況が一変してしまう。まず、TabBar の追加の仕方がわからない。さらに、storyboard に追加しても build すると error 発生で萎える。一週間ぐらい悩んで、ようやく error にならないようになりました。備忘のため手順を記しておこうと思います。

  1. テンプレートでは、Master-Detail App を選択します。Use CoreData のチェックを入れることを忘れないようにします。
  2. Main.storyboard を開きます。UISplitViewController が Storyboard Entry Point になっていますが、そのままで大丈夫です。UITabBarController を追加するため、Master の navigation controller を選択します。UISplitViewController からは 2 つの segue が設定されていますが、Master の方を選択します。
  3. メニューから、Editor > Embed in > Tab Bar Controller を選択します。余談ですが、UISplitViewController を選択した状態でこのメニューは選べませんので安心です。メニューを選択すると、UITabBarController が追加されます。
  4. splitView から detail に relationship が設定されているのでこれを削除します。削除する理由は error になるからなのですが、回避方法がわからないのでひとまず削除します。
  5. 試しにこの状態で build してみます。build は success となりますが、simulator ではアプリ起動直後に error になります。error の原因は色々ありますが、tabBar を追加したので、topViewController の指定が適切ではないためです(ということがようやく分かりました)。なので、適切に指定するように template の内容を変更します。
  6. AppDelegate.m を変更します。tabBarController を追加したので、topViewController の指定を変更します。正直、正しいのかさっぱり分かりませんが、変更箇所は以下の通り。

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

        // Override point for customization after application launch.

        UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;

        UITabBarController* tabBarController = [splitViewController.viewControllers lastObject];

        UINavigationController *navigationController = tabBarController.viewControllers[0];

        navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem;

        splitViewController.delegate = self;

        UINavigationController *masterNavigationController = tabBarController.viewControllers[0];

        MasterViewController *controller = (MasterViewController *)masterNavigationController.topViewController;

        controller.managedObjectContext = self.persistentContainer.viewContext;

     

        return YES;

    }

  7. 試しにここで build & run してみると、エラーになります。理由は、Master の ViewController を表示しようとした時に、tabBarController を追加したことを加味していないためです。MasterViewController.m の viewDidLoad method を変更します。変更箇所は次の通りです。
    変更前
       
    self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
    変更後
      
    self.detailViewController = (DetailViewController *)[[self.tabBarController.viewControllers lastObject] topViewController];

とりあえずこれで build & run しても error にならなくなります。storyboard の detail relation をカットしてしまったので、iPad の動作検証で問題になる可能性大ですが、error にならなくなったのは精神衛生上非常によいことで、躓くまではしばらくこのまま進めてみよう。