@@ -26,10 +26,17 @@ public final class ViewHost: @unchecked Sendable {
2626 /// result across the bridge; tests read `evaluate()` directly instead.
2727 public var onStateChange : ( ( ) -> Void ) ?
2828
29+ /// The animation captured from the transaction at write time, carried to
30+ /// the (coalesced) evaluation the write scheduled.
31+ private var pendingAnimation : Animation ?
32+
2933 public init ( _ root: any View , reflector: StateReflector = MirrorStateReflector ( ) ) {
3034 self . root = root
3135 self . storage = StateStorage ( reflector: reflector)
3236 self . storage. onChange = { [ weak self] in
37+ if let animation = Transaction . _current {
38+ self ? . pendingAnimation = animation
39+ }
3340 self ? . onStateChange ? ( )
3441 }
3542 }
@@ -43,13 +50,24 @@ public final class ViewHost: @unchecked Sendable {
4350 // Track @Observable reads during evaluation: a later mutation of any
4451 // observed property schedules a re-evaluation, exactly like a @State
4552 // write. Re-arms itself because the change handler triggers evaluate().
46- return withObservationTracking {
53+ var node = withObservationTracking {
4754 Evaluator . resolve ( root, context)
4855 } onChange: { [ weak self] in
56+ if let animation = Transaction . _current {
57+ self ? . pendingAnimation = animation
58+ }
4959 self ? . onStateChange ? ( )
5060 }
5161 #else
52- return Evaluator . resolve ( root, context)
62+ var node = Evaluator . resolve ( root, context)
5363 #endif
64+ // A tree produced by a withAnimation write carries the animation on its
65+ // root; the interpreter eases changed modifier values while it applies.
66+ if let animation = pendingAnimation {
67+ pendingAnimation = nil
68+ node. props [ " animationCurve " ] = . string( animation. curve)
69+ node. props [ " animationDurationMs " ] = . double( animation. duration * 1000 )
70+ }
71+ return node
5472 }
5573}
0 commit comments