Large Titleを使いこなす3つの要点


概要

iphoneアプリにおけるモダンなUIを実現するための方法として、いかにApple公式アプリのUIをパクるかは重要です。
iOS11にてAppleの公式UIとしてLargeTitleというものが追加されました。
UINavigationBarが設定されたスクロールビューにてタイトルの位置と大きさをアニメーションでいい感じにしてくれます。

「ミュージック」におけるLargeTitle

しかし、LargeTitleは周囲のUIパーツを正しく扱わないと残念な感じになってしまいます。
今回は実装に取り入れる上で、つまづいた3つの点と解決策をまとめました。

記事の前提

  • iOS11以降
  • Master-Detail型の遷移(LargeTitleが設定されたScrollVIew→詳細ビュー)を伴うアプリ

LargeTitleの実装

LargeTitleは簡単に以下の設定で有効にすることができます.

LargeTitle.swift
 //title設定
  navigationItem.title = "title"

 //largeTitle表示
 navigationItem.largeTitleDisplayMode = .always
 navigationController?.navigationBar.prefersLargeTitles = true

 //laegeTitle(小)時の文字
 navigationController?.navigationBar.titleTextAttributes = [
     .foregroundColor: .white
 ]

 //laegeTitle(大)時の文字
 navigationController?.navigationBar.largeTitleTextAttributes = [
     .foregroundColor: .white,
     .font : UIFont.boldSystemFont(ofSize: 26.0)
 ]

具体的な実装は下記の記事にまとまっています
- ios11からのNavigationBarを実装する(largeTitleDisplayMode)

残念なLargeTitle

しかし、周囲のUIパーツを正しく扱わないと下記のような残念な感じになってしまいます.

具体的には以下の点がかっこ悪いです。
- LargeTitleとNavigationBarの間に残るボーダーライン
- ナビゲーションコントローラでの画面遷移時に黒い背景
- スクロール時にLargeTitleとNavigationBar間にどこかの背景色

解決策

LargeTitleとNavigationBarの間に残るボーダーライン

LargeTitleが設定されている場合は大抵の場合無い方がキレイだと思いますが、デフォルトでは消してくれません。

これは、navigationController.navigationBar.shadowImageに設定されているUIImageです。

LargeTitle.swift
 //largeTitle時のnavigationBarのボーダを消す。戻す場合はnil代入
 navigationController?.navigationBar.shadowImage = UIImage()

また、nilが代入されるとデフォルトのボーダーラインに戻るようです。
画面遷移後もNavigationControllerのインスタンスは残るため、
1画面だけ消したい場合じゃDelegateメソッドなどでnilを代入し直す必要があります。

ナビゲーションコントローラでの画面遷移時に黒い背景

結論から言えばこいつはNavigationController自身の持っているViewの色です。
これについては情報が少なく、UIパーツ1つ1つの色を変えていくことでようやく発見しました。
そもそもNavigationController自体がViewを持っているという事実が完全に盲点、、、
下記のように、好きな色に変更することができます。遷移先のビューの色と合わせるときれいですね。

LargeTitle.swift
 //largetitleの適用時の画面遷移でチラ見する黒背景はコイツ
 navigationController.view.backgroundColor = .white

スクロール時にLargeTitleとNavigationBar間にどこかの背景

LargeTitle設定時にスクロールをひっぱった際にどこかの背景が写ってしまいかっこ悪いです。
今回の実装ではCollectionViewを利用していたのですが、LargeTitleの変化アニメーションにひっぱられ、
なんとCollectionViewの背景色のマイナス領域が表示されていました。

CollectionViewに限らず、ScrollView上部とNavigationBarの色を揃えたい時はこの設定が必要です。ケースとしては特殊な例かも知れませんが、備忘録として書いておきます。

CollectionView自体の背景色は変えたくなかったため、
これは適当な高さのCALayerをマイナス領域にAddSublayerすることで解決しました。

LargeTitle.swift
//largetitleのスクロール時にcollectionviewの背景色が表示される現象対策
 let fillSpaceLayer = CALayer()
 fillSpaceLayer.backgroundColor = .white
 fillSpaceLayer.frame = CGRect(x:0, y:-100, width: self.view.frame.width, height: 100)
 collectionView.layer.addSublayer(fillSpaceLayer)

まとめ

こんな感じにかっこよくLargeTitleを使いこなすことができました。

LargeTitle.swift
 //largeTitle表示
 navigationItem.largeTitleDisplayMode = .always
 navigationController?.navigationBar.prefersLargeTitles = true

 //laegeTitle(小)時の文字色
 navigationController?.navigationBar.titleTextAttributes = [
     .foregroundColor: .white
 ]
 //laegeTitle(大)時の文字色
 navigationController?.navigationBar.largeTitleTextAttributes = [
     .foregroundColor: .white,
     .font : UIFont.boldSystemFont(ofSize: 26.0)
 ]

 //largeTitle時のnavigationBarのボーダを消す。戻す場合はnil代入
 avigationController?.navigationBar.shadowImage = UIImage()

 //largetitleの適用時の画面遷移でチラ見する黒背景はコイツ
 navigationController.view.backgroundColor = .white

 //largetitleのスクロール時にcollectionviewの背景色が表示される現象対策
 let fillSpaceLayer = CALayer()
 fillSpaceLayer.backgroundColor = .white
 fillSpaceLayer.frame = CGRect(x:0, y:-100, width: self.view.frame.width, height: 100)
 collectionView.layer.addSublayer(fillSpaceLayer)