画面遷移にUINavigationControllerを使うことは多いと思いますが、少し注意をしないとレイアウトが思い通りにならないことがあります。
自分の経験に基づいて、注意すべき点を2点あげます。
UINavigationController.navigationBar.isTranslucent
ナビゲーションバーを透過にするかのフラグです。と共に、ビューの開始位置を決めるフラグでもあります。標準ではtrue
になっています。
追記: コメントで指摘をいただきました。@dokubekoさん、ありがとうございます。
バーの
isTranslucent
がビューの面積をバーの下までのばすトリガーではありません。
ビューの面積をバーの下まで伸ばすかどうかはビューコントローラの
edgesForExtendedLayout
プロパティで指定します。これに関連し、バーが不透明な場合にもそうするかどうかをextendedLayoutIncludesOpaqueBars
で指定します。
isTranslucent
がビューの面積の拡張のトリガーのようにみえるのは単にedgesForExtendedLayout
にtop
が含まれてていてextendedLayoutIncludesOpaqueBars
がfalse
の状態で切り替えているからです。extendedLayoutIncludesOpaqueBars
をtrue
にすれば、isTranslucent
がfalse
でもビューはバーの下まで伸びます。
ビューの描画領域を伸ばすのはUIViewController.edgesForExtendedLayout
みたいです。こいつはデフォルトで.all
になっています。
extendedLayoutIncludesOpaqueBars
はデフォルトでfalse
になっています。
extendedLayoutIncludesOpaqueBars
をtrue
にしてみると、たしかに描画範囲がナビゲーションバーのところまで広がります。
edgesForExtendedLayout
の方は.top
と.bottom
しか設定できないのですが、両方チェックがついている状態だと.all
になります。
いずれか一つのみチェックをすると.top
または.bottom
になり、両方チェックを外すと一切拡張されなくなります。
.left
と.right
は一体・・・?また調べます。
とにかく、以下の記事は
edgesForExtendedLayout = .all
extendedLayoutIncludesOpaqueBars = false
という前提でお読みください。
例えばこんな画面を作ってみます。
ナビゲーションバーに隠れるように'見えないはず'というラベルを置き、中心に'動かす'ボタン、下に'した'ラベルを置きました。
動かすボタンは以下のように、ナビゲーションバーを出したり隠したりします。
@IBAction func switchNavigationbarHidden(_ sender: UIButton) {
let status = !self.navigationController!.isNavigationBarHidden
self.navigationController?.setNavigationBarHidden(status, animated: true)
}
まずはisTranslucent
がtrue
の状態で動かしてみます。
ナビゲーションバーが表示されている状態では、'見えないはず'ラベルは見えません。
ナビゲーションバーを隠すと、'見えないはず'ラベルが見えるようになりました。
以上から、isTranslucent
をtrue
にすると次のようなことが起きます。
- ナビゲーションバーが透過になります(例だとちょっとわかりにくいけど)
-
ViewController
のビューの描画範囲は、ナビゲーションバーで隠れる部分を含めて画面全体になります
次に、isTranslucent
をfalse
にしてみましょう。
画像ではStoryboard上でfalse
にしていますが、もちろんコードからしても構いません。
既にStoryboard上でも変化が起きています。先程はナビゲーションバーに隠されて見えなかった'見えないはず'ラベルが見えるようになりました。
さらに、'動かす'ボタンの位置も先程より少し下になりました。
実行してみます。
ナビゲーションバーが表示されている状態は、Storyboardで見たとおりです。
ナビゲーションバーを隠すと、'見えないはず'ラベルと'動かす'ボタンが少し上に移動しました!
以上から、isTranslucent
をfalse
にすると次のようなことが起きます。
- ナビゲーションバーが透過されなくなります
- ナビゲーションバーが表示されている状態では、
ViewController
のビューの描画範囲が狭くなります - ナビゲーションバーを隠すと、その分
ViewController
のビューの描画範囲が広がります
個人で開発する分にはそこまで問題になることはないと思いますが、複数人で開発する際、ある人はtrue
前提、またある人はfalse
前提だったりすると、レイアウトがおかしなことになります。
開発を始める前にすり合わせをしておきましょう。
UIViewController.automaticallyAdjustsScrollViewInsets
スクロールできるビューを置いたときに、スクロール開始位置を自動的にずらしてくれるありがたい(場合によっては迷惑な)機能です。標準ではtrue
になっています。
次はこんな画面を作ってみます。
画面いっぱいにTableView
を置いて、そこに一つTableViewCell
を追加しました。
TableView
はナビゲーションバーに隠れる部分まで領域があるのに、TableViewCell
はナビゲーションバーに隠れること無く表示されています。これがタイトルの自動調整機能なのです。
試しに、automaticallyAdjustsScrollViewInsets
をfalse
にしてみましょう。
画像ではStoryboard上でfalse
にしていますが、もちろんコードからしても構いません。
TableViewCell
がナビゲーションバーに隠されました。
この自動調節機能、別にナビゲーションバーに重ならないところにTableView
を配置しても、しっかり発動してくれます。
Storyboardで作業をしているときはautomaticallyAdjustsScrollViewInsets
の設定が目に見えますが、xibを作っているときなんかは要注意です。
ScrollView
を置いていて、contentSize
はScrollView
のframe
より小さいはずなのに、何故かスクロールが発生するなんてときは、automaticallyAdjustsScrollViewInsets
が悪さしている可能性があります。
おわりに
今となっては困ることはなくなりましたが、勉強し初めの頃はこういった暗黙の了解的なものにだいぶ苦しめられました。
ネットで得られる断片的な知識だけで開発しようとするとこういった状況に陥りやすい気がします。
公式ドキュメントを読めばいいんですが、なかなか面倒ですよね・・・
とりあえず、UINavigationController
を使うときは上記2点に気をつけましょう!