🎊

SwiftUIのViewの再生成をひと目で分かるようにする

2023/01/09に公開約2,600字

SwiftUIにおいてViewの再生成を検知する方法はいくつかありますが、端末を操作中にどのViewが再生成されたかぱっと分かればいいなと思い、デバッグ向けですがこんなものを作ってみました。

DragGestureを使った例ですが、Viewが再生成されると対象のViewからパーティクルが出ます。

パーティクルのコード

extension View {
    func showViewRegeneration(alignment: Alignment = .topTrailing) -> some View {
        self.overlay(alignment: alignment) {
            VStack { // Stackに包まないとtransitionが表示されない
                Circle()
                    .fill([.red, .blue, .green, .gray, .brown, .cyan, .accentColor, .cyan, .mint, .indigo, .pink, .primary, .purple, .yellow, .orange].randomElement()!)
                    .frame(width: 5, height: 5)
                    .id(UUID())
                    .transaction({ t in
                        // 強制的にアニメーションをつける
                        t.animation = .linear(duration: 0.5)
                    })
                    .transition(.asymmetric(insertion: .identity, removal:
                            .offset(CGSize(width: 30, height: 0).applying(
                                // 360°ランダムな方向へ
                                CGAffineTransform(rotationAngle: .random(in: 0...(2 * .pi)))
                            ))
                    ))
            }
        }
    }
}

この拡張のポイントはこんなところです

  1. .id(UUID())とすることでCircleを複数描画可能にする(これが無いと同一のCircleと判定されてしまう)
  2. .transactionで強制的にアニメーションをつける
  3. .transitionは非表示になるタイミングのみ360°ランダムな方向へoffsetをつける

gifに使ったView

struct SampleView: View {
    @GestureState private var drag = CGSize.zero
    var body: some View {
        Rectangle()
            .fill(.white)
            .shadow(radius: 4)
            .frame(width: 200, height: 200)
            .showViewRegeneration()
            .offset(drag)
            .gesture(
                DragGesture().updating($drag) { value, state, _ in
                    state = value.translation
                }
            )
    }
}

普通にデバッグする

一応、普通のデバッグ方法も書いておきます。

Self._printChanges()を使う

一番手軽なのはこちらです。

View内にlet _ = Self._printChanges()を入れることで何起因でViewが再生成されたか確認することができます。

もしくはbreakpointを貼ってpo Self._printChanges()しても良いです。

Instrumentsを使う

Viewの生成回数、生成タイミング、生成にかかった時間など調査したい場合はInstrumentsを使って確認ができます。

Xcode > Open Developer Tool > Instruments

SwiftUIを選択

Targetを選んでRecordingボタンで記録
ドラッグ中にSampleViewが125回生成されていることが分かります

Discussion

ログインするとコメントできます