LifecycleとUIのヒエラルキーを抑える
はじめに
前回の記事で、iOSアプリのレイアウトを決める方法として、Auto Layoutを紹介しました。
Auto Layoutの中でも、StoryBoardからInterface Builderを使って設定する方法、.xib
に書く方法、コードから直接指定する方法があったのですが、宣言的にコードとしてという観点からコードで書くことにしました。
とりわけ、StoryBoardのGUIから指定する方法が宣言的ではないと言っているわけではないですが、コードとしてViewControllerに書いた方が真っ当でしょう。
今回はAutoLayoutに重要なView
だったり、Safearea
などを調べ、制約とどのような関連があるかを学べられるといいなと思います。
ViewControllerのライフサイクル
まず、Viewがどのように生成されて、部品がマウントされて、削除されるのかを知る必要があります。
これはいつ、どのような制約をかければいいかに繋がってきます。
アプリ開発をしていると以下のような状況にしばし、直面します。
- Viewのレイアウトが崩れてる
- なんかカクカクする
- チラつく部品がある
このような問題はViewのライフサイクルを理解せずにコードを書いていることが原因のことが多々あります。
Viewのライフサイクルの中で与えられるメソッドは以下の通りです。
ここでViewが読み込まれる時に何が起きるかを解説しまう。
viewDidLoad
Viewがライフサイクルに取り込まれてからをこのメソッドを使うことができる。 Viewがロードされると呼び出される。ユーザーが見えるようになるまでにデータを揃えたりするのに使える。
参考文献にあるこの関数でするべきものは
- ネットワークコール
- UIを構築
- 他のタスクを走らせる
です。私の場合は
self.view.addSubview(imageView) // MARK: - Inputs self.emailTextField.textColor = UIColor.black self.passwordTextField.textColor = UIColor.black self.emailTextField.placeholder = "Email" self.passwordTextField.placeholder = "Password" self.view.addSubview(emailTextField) self.view.addSubview(passwordTextField) // MARK: - Button self.button.setTitle("SIGN IN", for: UIControlState.normal) self.button.backgroundColor = UIColor.black button.addTarget(self, action: #selector(signinPressed(_:)), for: UIControlEvents.touchUpInside) self.view.addSubview(button)
のようになっています。
viewWillAppear
このメソッドはViewが見えるようになる直前に呼び出される。
この時は、Viewは作成されているのですが、まだマウントされていません。
このメソッドは、オーバーライドすることでViewのオブジェクトを見せたりするかしないかのフィルタリングなどに使うことができます。
viewWillLayoutSubviews
デフォルトでは何もしない。もしViewの枠が変わればサブビューも配置を変えるが、そのメソッドに変更を加えることができる。
viewDidLayoutSubviews
Subviewを調整したあとに呼び出される。
Subviewが調整し終わった後に何かをしたい時オーバーライドするべき。
たとえば、ViewControllerの表示、画面の向きなどの変更のときなのです。
viewDidAppear
Viewが見えたあとに呼び出される。
一般的には、Coredataへデータを保存したり、アニメーションなどを開始たり、ネットワークからデータを集め始めたりする。
viewWillDisappear
viewの階層から取り除かれるときに呼び出される。
状態を保存するにはするには最適である。
viewDidDisappear
viewが階層から取り除かれたときに呼び出される。
Viewアーキテクチャの基礎
ライフサイクルを理解したところでアーキテクチャを学ぼうと思います。
簡単な例だと以下のようなアーキテクチャになっています。
もっとも開発者の関心があることはUIを見せるUIView
クラスだと思います(とそれを継承したクラス)。
ここで階層的に定義される親ビューをsuperview
で、子ビューはsubview
と呼ばれます。
階層状になっており、
- スクリーン
- ウィンドウ
- ビュー
の状態です。
UIWindow、UIScreenとは
UIWindowはViewを管理して、ディスプレイに表示する「窓」としての役割を果たします。
また、UIScreenとは一言でいうと画面自体のことです。
ウィンドウは複数開くことができて、キーボードを使う時などもウィンドウが出てきてます。
ではUIViewとCALayerとは
もっとも参考になったのは以下の記事です。
UIViewは描画を行うクラスのことである。
そしてCALayerは例えばマスクの情報など描画する内容を管理するもので、UIViewを使うと必ず使っていることになる。 使うときはUIViewだけでは設定できないようなアニメーションなどを使いたい時です。
まとめ
ライフサイクルやスクリーン、ビューを知ることで、何をどこに書くかがより明瞭になった気がします。
余談
肥大化するviewDidLoadをベストプラクティス
この記事でも紹介したようにviewDidLoad
がかなりすることが大きく、コードが肥大化するケースがあります。
以下の記事でベストプラクティスが紹介されてありました。
再度、掲載します。
self.view.addSubview(imageView) // MARK: - Inputs self.emailTextField.textColor = UIColor.black self.passwordTextField.textColor = UIColor.black self.emailTextField.placeholder = "Email" self.passwordTextField.placeholder = "Password" self.view.addSubview(emailTextField) self.view.addSubview(passwordTextField) // MARK: - Button self.button.setTitle("SIGN IN", for: UIControlState.normal) self.button.backgroundColor = UIColor.black button.addTarget(self, action: #selector(signinPressed(_:)), for: UIControlEvents.touchUpInside) self.view.addSubview(button)
これをViewControllerのextention
として定義します。
ここではsetEmailTextField
だけやってみようと思います。
override func viewDidLoad(){ super.viewDidLoad() ... setEmailTextfield() ... } extention HogeViewController{ private func setEmailTextField(){ ... self.emailTextField.placeHolder = "Hello hoge" self.view.addSubView(emailText Field) }