新たに追加されたUIViewController viewIsAppearingの使い方

# 概要
新たに追加されたUIViewControllerの`viewIsAppearing(_:)`について説明します。

https://developer.apple.com/documentation/uikit/uiviewcontroller/4195485-viewisappearing

viewIsAppearingは、画面の初期表示時にUIをアップデートしたい時に使うものになっています。

## viewIsAppearingが使える環境について
viewIsAppearingはiOS 13から使えるAPIになります。ですが、開発環境がXcode 15ではないと使えないという、iOSとしては珍しい、後方互換がサポートされている新規APIになります。

## viewIsAppearingの特徴
[Choosing the appropriate callback](https://developer.apple.com/documentation/uikit/uiviewcontroller/4195485-viewisappearing#4230935)の図を見れば一目瞭然なのですが、viewIsAppearingの動作タイミングは、`viewWillAppear(_:)`と`viewDidAppear(_:)`の間のタイミングで行われます。
そして、`viewWillLayoutSubviews()`や`viewDidLayoutSubviews()`など他のレイアウトメソッドと違い、viewIsAppearingは一回の表示につき一回しか行われません。

viewIsAppearingは、viewDidAppearと違ってトランジション前に行われるので、画面表示前にビューに対して何か処理を行うのに有効です。
画面表示前という意味だとviewWillAppearも同じですが、リンク先の図や表を見る限りviewWillAppear実行時はレイアウト前の状態なのに対し、viewIsAppearingではすでにレイアウトが行われViewのサイズやTraitなどが確定した状態になっているので、画面サイズやSize Classesによって処理を変更したりできます。

# viewIsAppearingの使用例
例えば、画面幅によって縦方向と横方向が切り替わるUIStackViewを考えてみます。
UIStackViewにはUILabelが複数あり、一列に収まるようであれば一列表示、そうでなければ縦に並べるようなUIStackViewです。
サンプルにUIViewControllerを作成しました。

```swift
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
```swift
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        setupStackView()
    }
```

### 処理結果

![viewWillAppearだと横一列にUILabelが並ぶ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687960/e15ef7f5-e0e8-b84b-9c30-53ffa07fdb74.png)

このコードを実装しても、viewWillAppearではまだ画面幅が正確に確定していない状態なので表示は一列に収まると判断されて横並びで省略されてしまい、期待した結果となりません。

## viewDidAppear
```swift
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        setupStackView()
    }
```

![viewDidAppearだと遷移中は横一列にUILabelが並んでしまう](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687960/5b66e909-932e-a628-a31a-a61d3919408a.gif)

viewDidAppearで実装した場合、最終的には縦方向にUILabelが並びますが、トランジション中は一列に横並びすることになります。

## viewIsAppearing
```swift
    override func viewIsAppearing(_ animated: Bool) {
        super.viewIsAppearing(animated)
        setupStackView()
    }
```

![Simulator Screenshot - iPhone 15 Pro - 2023-10-06 at 09.30.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687960/52f8bc76-8f36-fc3f-b2f7-f0eeb8076b22.png)

viewIsAppearingで実装した場合、viewWillAppearやviewDidAppearで起こった問題は起こらず、正しく縦に並びます。

UILabelの文字数を減らした場合は横にレイアウトされています。

![Simulator Screenshot - iPhone 15 Pro - 2023-10-06 at 09.31.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687960/abc2ebbd-83a1-f199-89d9-986a0a9e0d25.png)

# 参考

https://developer.apple.com/videos/play/wwdc2023/10055/