@@ -12,7 +12,7 @@ import SDWebImage
12
12
#if !os(watchOS)
13
13
14
14
/// A coordinator object used for `AnimatedImage`native view bridge for UIKit/AppKit.
15
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
15
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
16
16
public final class AnimatedImageCoordinator : NSObject {
17
17
18
18
/// Any user-provided object for actual coordinator, such as delegate method, taget-action
@@ -25,7 +25,7 @@ public final class AnimatedImageCoordinator: NSObject {
25
25
}
26
26
27
27
/// Data Binding Object, only properties in this object can support changes from user with @State and refresh
28
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
28
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
29
29
final class AnimatedImageModel : ObservableObject {
30
30
enum Kind {
31
31
case url
@@ -53,7 +53,7 @@ final class AnimatedImageModel : ObservableObject {
53
53
}
54
54
55
55
/// Loading Binding Object, only properties in this object can support changes from user with @State and refresh
56
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
56
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
57
57
final class AnimatedLoadingModel : ObservableObject {
58
58
@Published var image : PlatformImage ? // loaded image, note when progressive loading, this will published multiple times with different partial image
59
59
@Published var isLoading : Bool = false // whether network is loading or cache is querying, should only be used for indicator binding
@@ -66,7 +66,7 @@ final class AnimatedLoadingModel : ObservableObject {
66
66
}
67
67
68
68
/// Completion Handler Binding Object, supports dynamic @State changes
69
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
69
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
70
70
final class AnimatedImageHandler : ObservableObject {
71
71
// Completion Handler
72
72
@Published var successBlock : ( ( PlatformImage , Data ? , SDImageCacheType ) -> Void ) ?
@@ -78,7 +78,7 @@ final class AnimatedImageHandler: ObservableObject {
78
78
}
79
79
80
80
/// Layout Binding Object, supports dynamic @State changes
81
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
81
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
82
82
final class AnimatedImageLayout : ObservableObject {
83
83
var contentMode : ContentMode ?
84
84
var aspectRatio : CGFloat ?
@@ -90,7 +90,7 @@ final class AnimatedImageLayout : ObservableObject {
90
90
}
91
91
92
92
/// Configuration Binding Object, supports dynamic @State changes
93
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
93
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
94
94
final class AnimatedImageConfiguration : ObservableObject {
95
95
var incrementalLoad : Bool ?
96
96
var maxBufferSize : UInt ?
@@ -106,7 +106,7 @@ final class AnimatedImageConfiguration: ObservableObject {
106
106
}
107
107
108
108
/// A Image View type to load image from url, data or bundle. Supports animated and static image format.
109
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
109
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
110
110
public struct AnimatedImage : PlatformViewRepresentable {
111
111
@ObservedObject var imageModel : AnimatedImageModel
112
112
@ObservedObject var imageHandler = AnimatedImageHandler ( )
@@ -275,6 +275,8 @@ public struct AnimatedImage : PlatformViewRepresentable {
275
275
self . imageModel. placeholderView? . isHidden = false
276
276
self . imageHandler. failureBlock ? ( error ?? NSError ( ) )
277
277
}
278
+ // Finished loading, async
279
+ finishUpdateView ( view, context: context, image: image)
278
280
}
279
281
}
280
282
@@ -361,22 +363,9 @@ public struct AnimatedImage : PlatformViewRepresentable {
361
363
break // impossible
362
364
}
363
365
364
- #if os(macOS)
365
- if self . isAnimating != view. wrapped. animates {
366
- view. wrapped. animates = self . isAnimating
367
- }
368
- #else
369
- if self . isAnimating != view. wrapped. isAnimating {
370
- if self . isAnimating {
371
- view. wrapped. startAnimating ( )
372
- } else {
373
- view. wrapped. stopAnimating ( )
374
- }
375
- }
376
- #endif
366
+ // Finished loading, sync
367
+ finishUpdateView ( view, context: context, image: view. wrapped. image)
377
368
378
- configureView ( view, context: context)
379
- layoutView ( view, context: context)
380
369
if let viewUpdateBlock = imageHandler. viewUpdateBlock {
381
370
viewUpdateBlock ( view. wrapped, context)
382
371
}
@@ -394,6 +383,17 @@ public struct AnimatedImage : PlatformViewRepresentable {
394
383
}
395
384
}
396
385
386
+ func finishUpdateView( _ view: AnimatedImageViewWrapper , context: Context , image: PlatformImage ? ) {
387
+ // Finished loading
388
+ if let imageSize = image? . size {
389
+ view. imageSize = imageSize
390
+ } else {
391
+ view. imageSize = nil
392
+ }
393
+ configureView ( view, context: context)
394
+ layoutView ( view, context: context)
395
+ }
396
+
397
397
func layoutView( _ view: AnimatedImageViewWrapper , context: Context ) {
398
398
// AspectRatio && ContentMode
399
399
#if os(macOS)
@@ -442,9 +442,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
442
442
#endif
443
443
444
444
// Resizable
445
- if let _ = imageLayout. resizingMode {
446
- view. resizable = true
447
- }
445
+ view. resizingMode = imageLayout. resizingMode
448
446
449
447
// Animated Image does not support resizing mode and rendering mode
450
448
if let image = view. wrapped. image {
@@ -587,11 +585,26 @@ public struct AnimatedImage : PlatformViewRepresentable {
587
585
} else {
588
586
view. wrapped. playbackMode = . normal
589
587
}
588
+
589
+ // Animation
590
+ #if os(macOS)
591
+ if self . isAnimating != view. wrapped. animates {
592
+ view. wrapped. animates = self . isAnimating
593
+ }
594
+ #else
595
+ if self . isAnimating != view. wrapped. isAnimating {
596
+ if self . isAnimating {
597
+ view. wrapped. startAnimating ( )
598
+ } else {
599
+ view. wrapped. stopAnimating ( )
600
+ }
601
+ }
602
+ #endif
590
603
}
591
604
}
592
605
593
606
// Layout
594
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
607
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
595
608
extension AnimatedImage {
596
609
597
610
/// Configurate this view's image with the specified cap insets and options.
@@ -630,70 +643,8 @@ extension AnimatedImage {
630
643
}
631
644
}
632
645
633
- // Aspect Ratio
634
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
635
- extension AnimatedImage {
636
- func setImageLayoutAspectRatio( _ aspectRatio: CGFloat ? , contentMode: ContentMode ) {
637
- self . imageLayout. aspectRatio = aspectRatio
638
- self . imageLayout. contentMode = contentMode
639
- }
640
-
641
- /// Constrains this view's dimensions to the specified aspect ratio.
642
- /// - Parameters:
643
- /// - aspectRatio: The ratio of width to height to use for the resulting
644
- /// view. If `aspectRatio` is `nil`, the resulting view maintains this
645
- /// view's aspect ratio.
646
- /// - contentMode: A flag indicating whether this view should fit or
647
- /// fill the parent context.
648
- /// - Returns: A view that constrains this view's dimensions to
649
- /// `aspectRatio`, using `contentMode` as its scaling algorithm.
650
- @ViewBuilder
651
- public func aspectRatio( _ aspectRatio: CGFloat ? = nil , contentMode: ContentMode ) -> some View {
652
- // The `SwifUI.View.aspectRatio(_:contentMode:)` says:
653
- // If `aspectRatio` is `nil`, the resulting view maintains this view's aspect ratio
654
- // But 1: there are no public API to declare what `this view's aspect ratio` is
655
- // So, if we don't override this method, SwiftUI ignore the content mode on actual ImageView
656
- // To workaround, we want to call the default `SwifUI.View.aspectRatio(_:contentMode:)` method
657
- // But 2: there are no way to call a Protocol Extention default implementation in Swift 5.1
658
- // So, we directly call the implementation detail modifier instead
659
- // Fired Radar: FB7413534
660
- let _ = self . setImageLayoutAspectRatio ( aspectRatio, contentMode: contentMode)
661
- if let aspectRatio {
662
- self . modifier ( _AspectRatioLayout ( aspectRatio: aspectRatio, contentMode: contentMode) )
663
- } else {
664
- self
665
- }
666
- }
667
-
668
- /// Constrains this view's dimensions to the aspect ratio of the given size.
669
- /// - Parameters:
670
- /// - aspectRatio: A size specifying the ratio of width to height to use
671
- /// for the resulting view.
672
- /// - contentMode: A flag indicating whether this view should fit or
673
- /// fill the parent context.
674
- /// - Returns: A view that constrains this view's dimensions to
675
- /// `aspectRatio`, using `contentMode` as its scaling algorithm.
676
- public func aspectRatio( _ aspectRatio: CGSize , contentMode: ContentMode ) -> some View {
677
- return self . aspectRatio ( aspectRatio. width / aspectRatio. height, contentMode: contentMode)
678
- }
679
-
680
- /// Scales this view to fit its parent.
681
- /// - Returns: A view that scales this view to fit its parent,
682
- /// maintaining this view's aspect ratio.
683
- public func scaledToFit( ) -> some View {
684
- return self . aspectRatio ( nil , contentMode: . fit)
685
- }
686
-
687
- /// Scales this view to fill its parent.
688
- /// - Returns: A view that scales this view to fit its parent,
689
- /// maintaining this view's aspect ratio.
690
- public func scaledToFill( ) -> some View {
691
- return self . aspectRatio ( nil , contentMode: . fill)
692
- }
693
- }
694
-
695
646
// AnimatedImage Modifier
696
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
647
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
697
648
extension AnimatedImage {
698
649
699
650
/// Total loop count for animated image rendering. Defaults to nil.
@@ -770,7 +721,7 @@ extension AnimatedImage {
770
721
}
771
722
772
723
// Completion Handler
773
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
724
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
774
725
extension AnimatedImage {
775
726
776
727
/// Provide the action when image load fails.
@@ -802,7 +753,7 @@ extension AnimatedImage {
802
753
}
803
754
804
755
// View Coordinator Handler
805
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
756
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
806
757
extension AnimatedImage {
807
758
808
759
/// Provide the action when view representable create the native view.
@@ -839,7 +790,7 @@ extension SDWebImageIndicator where Self == SDWebImageProgressIndicator {
839
790
}
840
791
841
792
// Web Image convenience, based on UIKit/AppKit API
842
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
793
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
843
794
extension AnimatedImage {
844
795
845
796
/// Associate a indicator when loading image with url
@@ -860,7 +811,7 @@ extension AnimatedImage {
860
811
}
861
812
862
813
#if DEBUG
863
- @available ( iOS 14 . 0 , OSX 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
814
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
864
815
struct AnimatedImage_Previews : PreviewProvider {
865
816
static var previews : some View {
866
817
Group {
0 commit comments