diff --git a/Sources/OpenSwiftUICore/Animation/Transition/Transition.swift b/Sources/OpenSwiftUICore/Animation/Transition/Transition.swift index 586931d84..6828c6b20 100644 --- a/Sources/OpenSwiftUICore/Animation/Transition/Transition.swift +++ b/Sources/OpenSwiftUICore/Animation/Transition/Transition.swift @@ -153,10 +153,14 @@ package struct ApplyTransitionModifier: PrimitiveViewModifier, M inputs: _ViewInputs, body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs ) -> _ViewOutputs { - .init() + TransitionType.makeView( + view: _GraphValue(Child(modifier: modifier.value)), + inputs: inputs, + body: body + ) } - nonisolated package static func _viewListCount( + nonisolated package static func viewListCount( inputs: _ViewListCountInputs, body: (_ViewListCountInputs) -> Int? ) -> Int? { diff --git a/Tests/OpenSwiftUICoreTests/Animation/Transition/TransitionTests.swift b/Tests/OpenSwiftUICoreTests/Animation/Transition/TransitionTests.swift new file mode 100644 index 000000000..5f43a1170 --- /dev/null +++ b/Tests/OpenSwiftUICoreTests/Animation/Transition/TransitionTests.swift @@ -0,0 +1,66 @@ +// +// TransitionTests.swift +// OpenSwiftUICoreTests + +import Foundation +import OpenAttributeGraphShims +@_spi(ForOpenSwiftUIOnly) @testable import OpenSwiftUICore +import OpenSwiftUITestsSupport +import Testing + +@MainActor +@Suite(.tags(.aigc), .disabled(if: attributeGraphVendor == .oag)) +struct TransitionTests { + @Test + func viewListCountWitnessPreservesBodyCount() { + var bodyCalls = 0 + let count = viewListCount( + ApplyTransitionModifier.self, + inputs: _ViewListCountInputs(.init(.invalid)) + ) { _ in + bodyCalls += 1 + return 2 + } + #expect(count == 2) + #expect(bodyCalls == 1) + } + + @Test(arguments: [TransitionPhase.willAppear, .identity, .didDisappear]) + func combinedTransitionPreservesContentLayout(phase: TransitionPhase) { + checkLayout( + transition: MoveTransition(edge: .leading).combined(with: .opacity), + phase: phase + ) + } + + @Test(arguments: [TransitionPhase.willAppear, .identity, .didDisappear]) + func asymmetricTransitionPreservesContentLayout(phase: TransitionPhase) { + checkLayout( + transition: AsymmetricTransition( + insertion: MoveTransition(edge: .leading).combined(with: .opacity), + removal: OpacityTransition() + ), + phase: phase + ) + } + + private func checkLayout(transition: T, phase: TransitionPhase) { + let content = Color.red + .frame(width: 32, height: 48) + .modifier(ApplyTransitionModifier(transition: transition, phase: phase)) + Update.perform { + let graph = ViewGraph(rootViewType: type(of: content), requestedOutputs: .layout) + defer { graph.invalidate() } + graph.setRootView(content) + #expect(graph.sizeThatFits(.unspecified) == CGSize(width: 32, height: 48)) + } + } + + private func viewListCount( + _ modifier: M.Type, + inputs: _ViewListCountInputs, + body: (_ViewListCountInputs) -> Int? + ) -> Int? { + M._viewListCount(inputs: inputs, body: body) + } +}