🎊
SwiftUIのViewの再生成をひと目で分かるようにする
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)))
))
))
}
}
}
}
この拡張のポイントはこんなところです
- .id(UUID())とすることでCircleを複数描画可能にする(これが無いと同一のCircleと判定されてしまう)
- .transactionで強制的にアニメーションをつける
- .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