UIViewControllerのライフサイクルでハマったこと

#はじめに
私が始めてのiOSアプリを作成した際にハマった問題について書きました。
始めての投稿ということで大変読みにくく、また間違って解釈してしまっている箇所もあるかもしれませんがよろしければご覧ください。これからSwift勉強しようと思っている人、今まさに勉強している人にとって少しでも役立つ記事であれば幸いです。


##UIImageViewを丸の形にしようとしたらハマった
`UIImageView`を丸の形にしようとしてハマった問題についての解説です。(`AutoLayout`を使用している場合のみこの問題が発生します。)
私まず最初に以下のようなコードで`ImageView`を丸にしようとしました。



```swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var ImageView: UIImageView!
    override func viewDidLoad() {
        super.viewDidLoad()
        ImageView.backgroundColor = UIColor.black
        ImageView.translatesAutoresizingMaskIntoConstraints = false
        ImageView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 200).isActive = true
        ImageView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 147).isActive = true
        ImageView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -147).isActive = true
        ImageView.heightAnchor.constraint(equalTo: ImageView.widthAnchor).isActive = true
        ImageView.layer.masksToBounds = false
        ImageView.layer.cornerRadius = ImageView.frame.height * 0.5
        ImageView.clipsToBounds = true
        
    }
}
```


このコードでは`ImageView`に以下のような制約が設定されています。

1.`ImageView`を全体画面の上端から200ptの位置に置く
2.`ImgaeView`の左端は全体画面の左端から147ptの位置に置く
3.`ImageView`の右端は全体画面の右端から-147ptの位置に置く
4.`ImageView`の高さを横幅と一緒にする

2と3の制約を設定することで`ImageView`の横幅の値が画面全体の横幅の大きさによって変わってきます。(例えばiPhone8とiPhone11では画面の大きさが違いますよね、この制約によって`ImageView`も8か11かによって大きさが変わってきます。)
しかし、このコードでは丸ではなく変な形になってしまいました。
いろいろ調べてみた結果以下のコードを実行するタイミングが原因でした。

```swift
   ImageView.layer.masksToBounds = false
   ImageView.layer.cornerRadius = ImageView.frame.height * 0.5
   ImageView.clipsToBounds = true
```

このコードは`ImageView`の`cornerRadius`を自身の高さ(または横幅)の半分に設定することで`ImageView`の形を丸にしようとしています。
ではなぜ丸ではなく変な形になるのかその原因は**UIViewContorollerのライフサイクル**にあります。


###UIViewControllerのライフサイクル
ViewControllerに遷移してからViewを表示するまでには多くのメソッドが順番に実行されています。


***UIViewControllerのライフサイクル:***
loadView -> viewDidLoad -> viewWillApper -> viewWillLayoutSubViews -> viewDidLayoutSubViews -> viewDidApper

※他にも実行されているメソッドはありますが今回は省略しました。

大雑把ですがこのようなメソッドが順番に実行されています。
この辺りのメソッドはおそらくUdemyの講座なんかでも出でくるメソッドだと思うのですが、これ以外にも実行されているメソッドがあり、今回の問題で重要なメソッドが以下の二つです。


####updateConstraints()
⚫︎設定した`AutoLayout`の制約の更新を実行するメソッドです。

####layoutSubviews()
⚫︎制約に基づいて部品のサイズと位置を決定するメソッドです。


これを踏まえたライフサイクルは以下のようになります。

loadView -> viewDidLoad -> viewWillApper -> updateConstraints -> viewWillLayoutSubviews -> layoutSubviews -> viewDidLayoutSubviews -> viewDidApper



ではもう一度さっきのコードをみてみましょう。

```swift
   ImageView.layer.masksToBounds = false
   ImageView.layer.cornerRadius = ImageView.frame.height * 0.5
   ImageView.clipsToBounds = true
```

`ImageView`の`cornerRadius`を設定するコードで`ImageView`の高さを取得していますが、制約が反映された`ImageView`のサイズ、位置を取得するには、それらを計算し決定するメソッドである`layoutSubviews()`が実行された後でないといけないのです。
ですから、このコードは`viewDidLoad`や`viewWillApper`ではなく`viewDidLayoutSubviews`で実行しなければいけません。

書き直したコードがこちらです。


```swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var ImageView: UIImageView!
    override func viewDidLoad() {
        super.viewDidLoad()
        ImageView.backgroundColor = UIColor.black
        ImageView.translatesAutoresizingMaskIntoConstraints = false
        ImageView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 200).isActive = true
        ImageView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 147).isActive = true
        ImageView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -147).isActive = true
        ImageView.heightAnchor.constraint(equalTo: ImageView.widthAnchor).isActive = true
        
    }
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        ImageView.layer.masksToBounds = false
        ImageView.layer.cornerRadius = ImageView.frame.height * 0.5
        ImageView.clipsToBounds = true
    }

}
```




#最後に
今回の問題以外にもUIViewControllerのライフサイクルに関して悩んだ所がアプリを作っている中で多々あったのでもっと早くに勉強しておくべきだった後悔しました。
UIViewControllerのライフサイクルについては下記の記事でたいへんわかりやすく解説してくれており、私もこの記事のおかげで今回の問題を解決することができました。
[UIKitのView表示ライフサイクルを理解する](https://qiita.com/shtnkgm/items/f133f73baaa71172efb2)
ここまで読んでいただきありがとうございました。