Skip to content

Commit 581a7e0

Browse files
authored
Add View.transition API support (#571)
1 parent 28977dd commit 581a7e0

File tree

2 files changed

+113
-5
lines changed

2 files changed

+113
-5
lines changed

Sources/OpenSwiftUICore/Animation/Transition/TransitionTraitKey.swift

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,72 @@
55
// Audited for 6.5.4
66
// Status: Complete
77

8-
// MARK: - ViewTraitCollection + canTransition
8+
// MARK: - View + transition
9+
10+
@available(OpenSwiftUI_v1_0, *)
11+
extension View {
12+
13+
/// Associates a transition with the view.
14+
///
15+
/// When this view appears or disappears, the transition will be applied to
16+
/// it, allowing for animating it in and out.
17+
///
18+
/// The following code will conditionally show MyView, and when it appears
19+
/// or disappears, will use a slide transition to show it.
20+
///
21+
/// if isActive {
22+
/// MyView()
23+
/// .transition(.slide)
24+
/// }
25+
/// Button("Toggle") {
26+
/// withAnimation {
27+
/// isActive.toggle()
28+
/// }
29+
/// }
30+
@inlinable
31+
@_disfavoredOverload
32+
nonisolated public func transition(_ t: AnyTransition) -> some View {
33+
return _trait(TransitionTraitKey.self, t)
34+
}
35+
36+
/// Associates a transition with the view.
37+
///
38+
/// When this view appears or disappears, the transition will be applied to
39+
/// it, allowing for animating it in and out.
40+
///
41+
/// The following code will conditionally show MyView, and when it appears
42+
/// or disappears, will use a custom RotatingFadeTransition transition to
43+
/// show it.
44+
///
45+
/// if isActive {
46+
/// MyView()
47+
/// .transition(RotatingFadeTransition())
48+
/// }
49+
/// Button("Toggle") {
50+
/// withAnimation {
51+
/// isActive.toggle()
52+
/// }
53+
/// }
54+
@available(OpenSwiftUI_v5_0, *)
55+
@_alwaysEmitIntoClient
56+
nonisolated public func transition<T>(_ transition: T) -> some View where T: Transition {
57+
self.transition(AnyTransition(transition))
58+
}
59+
}
60+
61+
// MARK: - TransitionTraitKey
62+
63+
@available(OpenSwiftUI_v1_0, *)
64+
@usableFromInline
65+
struct TransitionTraitKey: _ViewTraitKey {
66+
@inlinable
67+
static var defaultValue: AnyTransition { .opacity }
68+
}
69+
70+
@available(*, unavailable)
71+
extension TransitionTraitKey: Sendable {}
72+
73+
// MARK: - CanTransitionTraitKey
974

1075
@available(OpenSwiftUI_v1_0, *)
1176
@usableFromInline
@@ -17,6 +82,8 @@ struct CanTransitionTraitKey: _ViewTraitKey {
1782
@available(*, unavailable)
1883
extension CanTransitionTraitKey: Sendable {}
1984

85+
// MARK: - ViewTraitCollection + canTransition
86+
2087
extension ViewTraitCollection {
2188
package var canTransition: Bool {
2289
get { self[CanTransitionTraitKey.self] }
@@ -26,10 +93,6 @@ extension ViewTraitCollection {
2693

2794
// MARK: - ViewTraitCollection + transition
2895

29-
struct TransitionTraitKey: _ViewTraitKey {
30-
static var defaultValue: AnyTransition { .opacity }
31-
}
32-
3396
extension ViewTraitCollection {
3497
package var transition: AnyTransition {
3598
self[TransitionTraitKey.self]

Tests/OpenSwiftUICompatibilityTests/Modifier/ViewModifier/AppearanceActionModifierCompatibilityTests.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,49 @@ struct AppearanceActionModifierCompatibilityTests {
7373
}
7474
await #expect(Helper.result == "AFATDTDT")
7575
}
76+
77+
@Test("idTest with identity transition")
78+
func idTestWithIdentityTransition() async throws {
79+
enum Helper {
80+
@MainActor
81+
static var result = ""
82+
}
83+
84+
struct ContentView: View {
85+
@State private var toggle = false
86+
var continuation: UnsafeContinuation<Void, Never>
87+
88+
var body: some View {
89+
Color.red
90+
.onAppear {
91+
if Helper.result.isEmpty {
92+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
93+
toggle.toggle()
94+
}
95+
} else {
96+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
97+
continuation.resume()
98+
}
99+
}
100+
Helper.result += "A"
101+
Helper.result += toggle ? "T" : "F"
102+
}
103+
.onDisappear {
104+
Helper.result += "D"
105+
Helper.result += toggle ? "T" : "F"
106+
}
107+
.transition(.identity)
108+
.id(toggle)
109+
}
110+
}
111+
112+
try await triggerLayoutWithWindow { continuation in
113+
PlatformHostingController(
114+
rootView: ContentView(
115+
continuation: continuation
116+
)
117+
)
118+
}
119+
await #expect(Helper.result == "AFDTATDT")
120+
}
76121
}

0 commit comments

Comments
 (0)