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

このネタで 4 回も書くとは思ってもいなかったのですが、より良い方法があったので、後学のため書いておこうと。

Master-Detail template に tabBarController を追加するのにどこに追加するのか?という問題があり、さらに、いい加減に追加しても、error になりまくるので萎えてしまうという悪循環。結局のところ、こうするのが一番だということがわかりました。

  1. Xcode で、Master-Detail template を選択。storyboard は自動的に作られているので、table view controller を選択し、メニューから Editor > embed in > Tab Bar Controller を選択すると、tabBarController が追加されます。storyboard はどうなっているかというと、split view controller - navigation controller - tab bar controller - table view とつながっています。また、detail では navigation が表示されています。繋ぎ方を間違えると、split view controller - master からの遷移では navigation が表示されず、detail からは表示されるという妙な状況に。どちらからも navigation が表示されることを確認します。
  2. tab bar controller を embed in した状態で、試しに build & run してみると error になります。原因は、AppDelegate.m に追加した tabBarController の記述が無いためです。error を回避するため、AppDelegate.m を次のように変更します:

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

    {

        // Override point for customization after application launch.

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

        UINavigationController *navigationController = [splitViewController.viewControllers lastObject];

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

        splitViewController.delegate = self;

     

        UINavigationController *masterNavigationController = splitViewController.viewControllers[0];

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

        

        UITabBarController* tabBarController = masterNavigationController.tabBarController.viewControllers[0];

        MasterViewController *controller = (MasterViewController *)tabBarController.viewControllers[0];

        

        controller.managedObjectContext = self.persistentContainer.viewContext;

        return YES;

    }

  3. 次に、この状態で試しに build & run してみると相変わらず error になります。かなり萎えます。原因は、MasterViewController.m で、managedObjectContext について教えてあげる必要があるためです。また、build & run に成功しても、navigation が表示されないため、navigation について考慮した記載にします。MasterViewController.m を次のように変更します:

    - (void)viewDidLoad

    {

        [super viewDidLoad];

        // Do any additional setup after loading the view, typically from a nib.

        self.tabBarController.navigationItem.leftBarButtonItem = self.editButtonItem;

     

        UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];

        self.tabBarController.navigationItem.rightBarButtonItem = addButton;

        self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];

        

        AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];

        self.managedObjectContext = appDelegate.persistentContainer.viewContext;

        

    }

これで error にならず navigation も表示されるようになります。

tab をもう一枚追加するにはどうするのかとか、iPad で検証すると error になりそうな予感がしますが、躓くまでこれで進めてみることにしよう。

 

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

前回は storyboard の繋ぎ方について言及しました。今回は appDelegate.m と masterViewController.m の変更箇所について記載します。

appDelegate.m の変更箇所は、

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOption

method です。tabBarController を追加したのでそれを考慮した作りにします。具体的には次の通り:

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

    UINavigationController* navigationController = [splitViewController.viewControllers lastObject];

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

    splitViewController.delegate = self;

    UINavigationController *masterNavigationController = splitViewController.viewControllers[0];

    UITabBarController* tabBarController = navigationController.topViewController;

    MasterViewController *controller = (MasterViewController *)masterNavigationController.tabBarController.viewControllers;

    controller.managedObjectContext = self.persistentContainer.viewContext;

    return YES;

tabBarController が navigationController に従属して、masterViewController もそれに倣ってという形。

ここまで書いて build & run してみると error。かなり萎えます。次に、masterViewController.m を変更。変更箇所は次の通り:

- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

    self.tabBarController.navigationController.toolbarHidden = NO;

    self.tabBarController.navigationItem.leftBarButtonItem = self.editButtonItem;

    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];

    self.tabBarController.navigationItem.rightBarButtonItem = addButton;

    self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];    

    AppDelegate* appDelegate = [[UIApplication sharedApplication] delegate];

    self.managedObjectContext = appDelegate.persistentContainer.viewContext;

}

見逃しがちなのが、tabBarController.navigationController とするところで、これがないと detail で navigation が表示されなくなる。さらに、最後の appDelegate の指定がないと error になってしまうので、已を得ず記載。

これで error は回避できるし、navigation も表示されるようになるので、しばらくこれで進めてみよう。 

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

やっぱりハマりました。Master-Detail template で、rootView が splitViewController になり、そこに tabBarController を追加したくてしたくてたまらなかったのですが、オレオレでやってみたら detail に画面遷移した折に navigationBar が表示されない事態に・・・それを回避するためいろいろ試してみたものの、さっぱり改善しません。

detail に navigationBar が表示されないと何が問題なのかというと、detail に遷移した後 master に戻れなくなってしまうのが難点。色々試してみたところ、ようやく解決しました。

まず、viewController の繋ぎ方ですが、splitViewController - navigationViewController - tabBarController の順で繋ぎます。前回は、splitViewController - tabBarController - navigationController の順でつないでいて、storyboard で確認した時点で navigationBar が表示されなくなっていた。navigationViewController を先にすると、storyboard で見ても明らかに navigation が表示されているので、これが正解なんだなと。よくわからないのが、メニューから embed を選んだら tabBar が先に来てしまうので、手動で繋ぎ買えてやる必要がある点。

ゴールまでには道のりが遠く、その後、appDelegate.m や materViewController.m を変更する必要がありますが、眠くなって来たのでまた次回書きたいと思います。

パタゴニアのセール

パタゴニアのセールに行ってきた。ここ、日本では結構強気で、ほとんど値引きなしで販売するアウトドアブランド。セールは通常年 2 回しかなくて、8 月、2 月が通例。しかも、値引率がかなりしょぼくて 20% Off ぐらい。Web アウトレットを小まめにみていると掘り出し物も見つかるでしょうが、予告なしで特価品が出るのでこまめにみるほど暇ではないので、自然と店舗のセールの時に買うことになる。

今回狙っていたのは、キャプリーンミッドウエイト。キャプリーンはサーマルウエイトを持っていて、冬のランニングの時に重宝していて、素晴らしいのは分かっていたのだけれど、サーマルの下に何を着るのか悩みがち。R1 も持っているのですが、同じく、ベースとなる肌着は何を着ればよいのかいつも悩んでしまいます。どうやらキャプリーンのライトウエイトかミッドウエイトだと、丁度よい感じがしていたのですが、高額すぎて普段は買えないので、セールの折に買ってしまおうと。ライトとミッドは悩みましたが、最近寒いのでミッドにしておこうと。

実店舗でのセール初日に仕事帰りに寄ったところ、すでに満員状態で、改めて人気なんだと再認識。肝心のミッドウエイトはどこだと探したら、値引きしていない・・・なんだよこれ、と思ったらありました、値引きしたのが。色によって、値引きするものしないものがあるようで、オリーブ色のは値引き、グレーや黒は値引きなしという状況でした。相変わらずセコいというか、潔さがないというか、セールの看板降ろせよと思いました。

ベースレイヤーなので、色はなんでもよいので値引きしたオリーブの買いました。レジでも結構イラっとすることが・・・まず、袋は有料で ¥100 だって。エコを意識しているんでしょうかね、それにしても ¥100 は高いだろ。いらないと言ってそのままカバンに放り込んでやったよ。なんか、古着を買っている気分だったね。あと、店員がすごい横柄というか、髭面は、まぁ、許すとしても言い方気をつけろよって感じ。売ってやるからさ、みたいで印象悪かったです。

家に帰ってから早速着てみました。やっぱいいね。暑くないんだけど寒くないという、絶妙な着心地。製品はいいのにね、売り方に共感できない、荒さしか見えないのは、ちょっと残念な感じです。この店舗だからか?店変えればいいのかな。

 

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 にならなくなったのは精神衛生上非常によいことで、躓くまではしばらくこのまま進めてみよう。

 

ヴェポライザーでシャグ:Bali Shag 赤

ヴェポライザーで shag、色々 shag を試しているものの、メンソールとか香りのついていない普通のタバコ味で吸いごたえのある shag はないのか色々試していますが、なかなかいいのが見つからない。

そんな中、昨日買ってきた Bali Shag 赤はかなりよい。吸いごたえありすぎ。正直、くらっときた。これいいね。いつもより少なめにしないときつくて吸えないレベル。

これだと、青、緑も試したくなってくるね。

オレオレ Shag ランキング

1. Colts Clear Menthol

2. Bali Shag rounded golden versinia

3. Stanley Ice Mint

4. Colts Nature

 

UITextField で表示するキーボードは Storyboard で設定できる

UITextField で表示する keyboard は、code から設定するものだとばかり思っていたのですが、Storyboard からも設定できることを今日知った・・・

UITextField を選択し、Show the Attributes Inspector を選択、Text Input Traits の中に、Keyboard Type という list があり、keyboard を指定できる。

せっかくなので、この方法でしばらくやってみよう。

ちなみに、keyboard type を number pad にして、数字の keyboard を出せば、数字しか入力できないのかと思ったら、そう甘くはない。copy & paste すると相変わらず alphabet も paste できてしまうので、入力文字制限のための delegate method で別途に書かないとならない。