1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

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

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

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かによって大きさが変わってきます。)
しかし、このコードでは丸ではなく変な形になってしまいました。
いろいろ調べてみた結果以下のコードを実行するタイミングが原因でした。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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表示ライフサイクルを理解する
ここまで読んでいただきありがとうございました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?