@@ -20,6 +20,23 @@ extension View {
2020 }
2121}
2222
23+ extension AnimatedImage {
24+ struct WrapperView : View & Inspectable {
25+ var name : String
26+ var bundle : Bundle ?
27+ @State var isAnimating : Bool
28+
29+ var onViewUpdate : ( Self , PlatformView , AnimatedImage . Context ) -> Void
30+
31+ var body : some View {
32+ AnimatedImage ( name: name, bundle: bundle, isAnimating: $isAnimating)
33+ . onViewUpdate { view, context in
34+ self . onViewUpdate ( self , view, context)
35+ }
36+ }
37+ }
38+ }
39+
2340class AnimatedImageTests : XCTestCase {
2441
2542 override func setUp( ) {
@@ -89,38 +106,50 @@ class AnimatedImageTests: XCTestCase {
89106
90107 func testAnimatedImageBinding( ) throws {
91108 let expectation = self . expectation ( description: " AnimatedImage binding control " )
92- let binding = Binding < Bool > ( wrappedValue: true )
93- var context : AnimatedImage . Context ?
94- let imageView = AnimatedImage ( name: " TestLoopCount.gif " , bundle: TestUtils . testImageBundle ( ) , isAnimating: binding)
95- . onViewCreate { _, c in
96- context = c
97- }
98- let introspectView = imageView. introspectAnimatedImage { animatedImageView in
109+ var viewUpdateCount = 0
110+ // Use wrapper to make the @Binding works
111+ let wrapperView = AnimatedImage . WrapperView ( name: " TestLoopCount.gif " , bundle: TestUtils . testImageBundle ( ) , isAnimating: true ) { wrapperView, view, context in
112+ viewUpdateCount += 1
113+ guard let animatedImageView = view as? SDAnimatedImageView else {
114+ XCTFail ( " AnimatedImage's view should be SDAnimatedImageView " )
115+ return
116+ }
99117 if let animatedImage = animatedImageView. image as? SDAnimatedImage {
100118 XCTAssertEqual ( animatedImage. animatedImageLoopCount, 1 )
101119 XCTAssertEqual ( animatedImage. animatedImageFrameCount, 2 )
102120 } else {
103- XCTFail ( " SDAnimatedImageView. image invalid " )
121+ XCTFail ( " AnimatedImage's image should be SDAnimatedImage " )
104122 }
105- #if os(iOS) || os(tvOS)
106- XCTAssertTrue ( animatedImageView. isAnimating)
123+ // View update times before stable from SwiftUI, different behavior for AppKit/UIKit
124+ #if os(macOS)
125+ let stableViewUpdateCount = 1
107126 #else
108- XCTAssertTrue ( animatedImageView . animates )
127+ let stableViewUpdateCount = 2
109128 #endif
110- binding. wrappedValue = false
111- XCTAssertFalse ( binding. wrappedValue)
112- XCTAssertFalse ( imageView. isAnimating)
113- // Currently ViewInspector's @Binding value update does not trigger `UIViewRepresentable.updateUIView`, mock here
114- imageView. updateView ( animatedImageView. superview as! AnimatedImageViewWrapper , context: context!)
115- #if os(iOS) || os(tvOS)
116- XCTAssertFalse ( animatedImageView. isAnimating)
117- #else
118- XCTAssertFalse ( animatedImageView. animates)
119- #endif
120- expectation. fulfill ( )
129+ switch viewUpdateCount {
130+ case stableViewUpdateCount:
131+ // #1 SwiftUI's own updateUIView call
132+ #if os(iOS) || os(tvOS)
133+ XCTAssertTrue ( animatedImageView. isAnimating)
134+ #else
135+ XCTAssertTrue ( animatedImageView. animates)
136+ #endif
137+ DispatchQueue . main. async {
138+ wrapperView. isAnimating = false
139+ }
140+ case stableViewUpdateCount + 1 :
141+ // #3 AnimatedImage's isAnimating @Binding trigger update (from above)
142+ #if os(iOS) || os(tvOS)
143+ XCTAssertFalse ( animatedImageView. isAnimating)
144+ #else
145+ XCTAssertFalse ( animatedImageView. animates)
146+ #endif
147+ expectation. fulfill ( )
148+ default : break
149+ // do not care
150+ }
121151 }
122- _ = try introspectView. inspect ( AnimatedImage . self)
123- ViewHosting . host ( view: introspectView)
152+ ViewHosting . host ( view: wrapperView)
124153 self . waitForExpectations ( timeout: 5 , handler: nil )
125154 }
126155
0 commit comments