次の簡単な例を考えてみましょう。
struct TestView: View {
@State private var enabled = false
var body: some View {
Circle()
.foregroundColor(.red)
.overlay(
Circle()
.foregroundColor(.blue)
.frame(width: 50, height: 50)
.animation(.spring())
)
.frame(width: 100, height: 100)
.offset(x: 0, y: enabled ? -50 : 50)
.animation(.easeIn(duration: 1.0))
.onTapGesture{
enabled.toggle()
}
}
}
結果の円をタップすると、次のアニメーションが生成されます。
ネストされた円は、独自のタイミング関数 (スプリング) で新しいグローバル位置にアニメーション化されますが、外側の円/親ビューは、easeInOut タイミング関数で新しいグローバル位置にアニメーション化されます。理想的には、修飾子が親ビューの抽象化レベルで機能する場合、すべての子が親のタイミング関数でアニメーション化されます (この場合はoffset
だけでなく、 のようなものもありますposition
)。
これはおそらく、SwiftUI レンダリング エンジンがレイアウト パスのビュー階層で影響を受けるすべての子の新しいグローバル プロパティを計算し、アタッチされた最も具体的なアニメーション修飾子に基づいて各プロパティへの変更をアニメーション化するために発生します (この場合、相対位置親内の子の値は変更されません)。これにより、そのビューのサブビューが独自の複雑なアニメーションを実行している可能性がある場合に、ビューを適切に変換するような単純なことを行うのが非常に困難になります (親ビューはそれを認識しておらず、実際に認識すべきではありません)。
私が気付いたもう 1 つの癖は、この特定の例では、offset モディファイアに直接アタッチされたままであるにもかかわらず、offset モディファイアのanimation(nil)
直前にモディファイアを追加すると、外側の円のアニメーションが壊れることです。.easeInOut
これは、これらの修飾子がどのように連鎖されているかについての私の理解に違反しています。(このソースによると) アニメーション修飾子は、次のネストされたアニメーション修飾子まで、それに伴うすべてのビューに適用されます。