はじめに
以前、UITextView
を継承してPlaceholder
付きのUITextView
を作ったときに、
- コードでサブクラスを生成・
Placeholder
のセットを行った場合 - storyboardでサブクラス生成・
Placeholder
のセットを行った場合
とで初期化直後の状態が異なっているときがありました。
サブクラスをstoryboardで生成した場合のプロパティの初期化のタイミングを理解していなかったことが原因だったためそのことについてメモ。
初期化のタイミング
検証用にUIViewを継承したサブクラスを作りました。
UIViewにUILabelをサブビューとして持たせて、@IBInspectable
でtitle
をセットできるようにしただけのものです。
@IBDesignable class CustomView: UIView {
let titleLabel = UILabel()
// -----------------------------------------------------------------
override var frame: CGRect {
didSet {
print("2. frame did set \(frame).")
}
}
override var backgroundColor: UIColor? {
didSet {
print("3. backgroundColor did set \(backgroundColor).")
}
}
// -----------------------------------------------------------------
override init(frame: CGRect) {
print("1. Will super.init(frame: frame)")
super.init(frame: frame)
print("4. Did super.init(frame: frame)")
config()
}
required init?(coder aDecoder: NSCoder) {
print("1. Will super.init(coder: aDecoder)")
super.init(coder: aDecoder)
print("4. Did super.init(coder: aDecoder)")
config()
}
func config() {
titleLabel.frame = CGRect(origin: .zero, size: CGSize(width: frame.width, height: frame.height / 2))
titleLabel.textColor = UIColor.red
titleLabel.textAlignment = .center
titleLabel.backgroundColor = UIColor.gray
self.addSubview(titleLabel)
print("5. Add subview titleLabel.")
}
// -----------------------------------------------------------------
@IBInspectable var title: String? {
didSet {
titleLabel.text = title
print("6. title did set \(title).")
}
}
}
以下を実行します。backgroundColor
とtitle
をセットします。これをコードとstoryboardでやったときの違いを見ます。
@IBOutlet weak var customView: CustomView!
override func viewDidLoad() {
super.viewDidLoad()
let customView2 = CustomView(frame: CGRect(x: 67, y: 250, width: 240, height: 128))
customView2.backgroundColor = UIColor.init(red: 1, green: 0.4, blue: 0, alpha: 1)
customView2.title = "Markdown"
view.addSubview(customView2)
}
出力結果はこのようになります。
// storyboard
1. Will super.init(coder: aDecoder)
2. frame did set (67.0, 92.0, 240.0, 128.0)
3. backgroundColor did set Optional(UIExtendedSRGBColorSpace 0 0.478431 1 1)
4. Did super.init(coder: aDecoder)
5. Add subview titleLabel.
6. title did set Optional("Qiita"). // subclassのプロパティ
// code
1. Will super.init(frame: frame)
2. frame did set (67.0, 250.0, 240.0, 128.0)
4. Did super.init(frame: frame)
5. Add subview titleLabel.
3. backgroundColor did set Optional(UIExtendedSRGBColorSpace 1 0.4 0 1)
6. title did set Optional("Markdown"). // subclassのプロパティ
この例では見た目に変化はないですが、ログによって初期化の流れに注意点があることに気づきました。
まずsuper.init
で親クラスのプロパティが初期化されますよね。
コードの方で3. backgroundColor did set
の位置が違うってのは、init
で初期化された後にコードでbackgroundColor
をセットしているためです。init(frame: frame)
で初期化した場合はframe
だけこのタイミングで初期化されるということです。
問題はここではなく、サブクラスで新しく設けたプロパティ(title
)を@IBInspectable
でセットしたときにどのタイミングで初期化されるかです。
筆者はframe
やbackgroundColor
が初期化されるタイミングと同じだと勝手に思っていました。
実際には、init
内の処理が終わった後になるようですね。コードで生成する場合は、init(frame: frame)
→backgroundColor
のセット→title
のセットと書いたコードの順序に従っているので誤解は生じませんが、storyboardで生成した場合はコードを見てもこの流れが認識しづらいという印象を受けました。