概要
新たに追加されたUIViewControllerのviewIsAppearing(_:)
について説明します。
viewIsAppearingは、画面の初期表示時にUIをアップデートしたい時に使うものになっています。
viewIsAppearingが使える環境について
viewIsAppearingはiOS 13から使えるAPIになります。ですが、開発環境がXcode 15ではないと使えないという、iOSとしては珍しい、後方互換がサポートされている新規APIになります。
viewIsAppearingの特徴
Choosing the appropriate callbackの図を見れば一目瞭然なのですが、viewIsAppearingの動作タイミングは、viewWillAppear(_:)
とviewDidAppear(_:)
の間のタイミングで行われます。
そして、viewWillLayoutSubviews()
やviewDidLayoutSubviews()
など他のレイアウトメソッドと違い、viewIsAppearingは一回の表示につき一回しか行われません。
viewIsAppearingは、viewDidAppearと違ってトランジション前に行われるので、画面表示前にビューに対して何か処理を行うのに有効です。
画面表示前という意味だとviewWillAppearも同じですが、リンク先の図や表を見る限りviewWillAppear実行時はレイアウト前の状態なのに対し、viewIsAppearingではすでにレイアウトが行われViewのサイズやTraitなどが確定した状態になっているので、画面サイズやSize Classesによって処理を変更したりできます。
viewIsAppearingの使用例
例えば、画面幅によって縦方向と横方向が切り替わるUIStackViewを考えてみます。
UIStackViewにはUILabelが複数あり、一列に収まるようであれば一列表示、そうでなければ縦に並べるようなUIStackViewです。
サンプルにUIViewControllerを作成しました。
class StackViewController: UIViewController {
private var stackView: UIStackView!
override func loadView() {
// ここの長さによって縦方向、横方向が切り替わる
let l1 = UILabel()
l1.text = "123456789"
let l2 = UILabel()
l2.text = "bbbbbbbbbb"
let l3 = UILabel()
l3.text = "ccccccccccccccccccc"
let stackView = UIStackView(arrangedSubviews: [l1,l2,l3])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.distribution = .fill
let view = UIView()
view.backgroundColor = .systemBackground
view.addSubview(stackView)
NSLayoutConstraint.activate([stackView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor),
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor)])
self.stackView = stackView
self.view = view
}
/// StackViewの内容によって、axisを切り替える
private func setupStackView() {
let desiredStackViewWidth = stackView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width
let availableWidth = view.readableContentGuide.layoutFrame.width
if desiredStackViewWidth > availableWidth {
stackView.axis = .vertical
} else {
stackView.axis = .horizontal
}
}
}
このコードを元に、いろんなイベントで確かめてみます。
viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setupStackView()
}
処理結果
このコードを実装しても、viewWillAppearではまだ画面幅が正確に確定していない状態なので表示は一列に収まると判断されて横並びで省略されてしまい、期待した結果となりません。
viewDidAppear
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setupStackView()
}
viewDidAppearで実装した場合、最終的には縦方向にUILabelが並びますが、トランジション中は一列に横並びすることになります。
viewIsAppearing
override func viewIsAppearing(_ animated: Bool) {
super.viewIsAppearing(animated)
setupStackView()
}
viewIsAppearingで実装した場合、viewWillAppearやviewDidAppearで起こった問題は起こらず、正しく縦に並びます。
UILabelの文字数を減らした場合は横にレイアウトされています。
参考