#はじめに
私が始めての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
このコードは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
ではもう一度さっきのコードをみてみましょう。
ImageView.layer.masksToBounds = false
ImageView.layer.cornerRadius = ImageView.frame.height * 0.5
ImageView.clipsToBounds = true
ImageView
のcornerRadius
を設定するコードでImageView
の高さを取得していますが、制約が反映されたImageView
のサイズ、位置を取得するには、それらを計算し決定するメソッドであるlayoutSubviews()
が実行された後でないといけないのです。
ですから、このコードはviewDidLoad
やviewWillApper
ではなく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表示ライフサイクルを理解する
ここまで読んでいただきありがとうございました。