diff --git a/Example/SDWebImageSwiftUIDemo/DetailView.swift b/Example/SDWebImageSwiftUIDemo/DetailView.swift index 3455fb35..ccdc2c87 100644 --- a/Example/SDWebImageSwiftUIDemo/DetailView.swift +++ b/Example/SDWebImageSwiftUIDemo/DetailView.swift @@ -13,6 +13,7 @@ struct DetailView: View { let url: String let animated: Bool @State var progress: CGFloat = 1 + @State var isAnimating: Bool = true var body: some View { VStack { @@ -24,7 +25,7 @@ struct DetailView: View { Spacer() HStack { if animated { - AnimatedImage(url: URL(string:url), options: [.progressiveLoad]) + AnimatedImage(url: URL(string:url), options: [.progressiveLoad], isAnimating: $isAnimating) .onProgress(perform: { (receivedSize, expectedSize) in // SwiftUI engine itself ensure the main queue dispatch if (expectedSize >= 0) { @@ -35,6 +36,9 @@ struct DetailView: View { }) .resizable() .scaledToFit() + .navigationBarItems(trailing: Button(isAnimating ? "Stop" : "Start") { + self.isAnimating.toggle() + }) } else { WebImage(url: URL(string:url), options: [.progressiveLoad]) .onProgress(perform: { (receivedSize, expectedSize) in diff --git a/README.md b/README.md index 290f42c5..85ffa5fc 100644 --- a/README.md +++ b/README.md @@ -85,20 +85,24 @@ var body: some View { ```swift var body: some View { Group { - AnimatedImage(url: URL(string: "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif")) // Network + // Network + AnimatedImage(url: URL(string: "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif")) .onFailure(perform: { (error) in // Error }) .scaledToFit() - AnimatedImage(data: try! Data(contentsOf: URL(fileURLWithPath: "/tmp/foo.webp"))) // Data + // Data + AnimatedImage(data: try! Data(contentsOf: URL(fileURLWithPath: "/tmp/foo.webp"))) .customLoopCount(1) - AnimatedImage(name: "animation1") // Bundle (not Asset Catalog) + // Bundle (not Asset Catalog) + AnimatedImage(name: "animation1", isAnimating: $isAnimating)) // Animation control binding .maxBufferSize(.max) } } ``` - [x] Supports network image as well as local data and bundle image +- [x] Supports animation control using the SwiftUI Binding - [x] Supports advanced control like loop count, incremental load, buffer size. Note: `AnimatedImage` supports both image url or image data for animated image format. Which use the SDWebImage's [Animated ImageView](https://github.com/SDWebImage/SDWebImage/wiki/Advanced-Usage#animated-image-50) for internal implementation. diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 89c3442b..747464a8 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -48,6 +48,46 @@ public struct AnimatedImage : PlatformViewRepresentable { var webOptions: SDWebImageOptions = [] var webContext: [SDWebImageContextOption : Any]? = nil + /// A Binding to control the animation. You can bind external logic to control the animation status. + /// True to start animation, false to stop animation. + @Binding public var isAnimating: Bool + + public init(url: URL?, placeholder: PlatformImage? = nil, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) { + self.init(url: url, placeholder: placeholder, options: options, context: context, isAnimating: .constant(true)) + } + + public init(url: URL?, placeholder: PlatformImage? = nil, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding) { + self._isAnimating = isAnimating + self.placeholder = placeholder + self.webOptions = options + self.webContext = context + self.imageModel.url = url + } + + public init(name: String, bundle: Bundle? = nil) { + self.init(name: name, bundle: bundle, isAnimating: .constant(true)) + } + + public init(name: String, bundle: Bundle? = nil, isAnimating: Binding) { + self._isAnimating = isAnimating + #if os(macOS) + let image = SDAnimatedImage(named: name, in: bundle) + #else + let image = SDAnimatedImage(named: name, in: bundle, compatibleWith: nil) + #endif + self.imageModel.image = image + } + + public init(data: Data, scale: CGFloat = 0) { + self.init(data: data, scale: scale, isAnimating: .constant(true)) + } + + public init(data: Data, scale: CGFloat = 0, isAnimating: Binding) { + self._isAnimating = isAnimating + let image = SDAnimatedImage(data: data, scale: scale) + self.imageModel.image = image + } + #if os(macOS) public typealias NSViewType = AnimatedImageViewWrapper #else @@ -90,6 +130,20 @@ public struct AnimatedImage : PlatformViewRepresentable { } } + #if os(macOS) + if self.isAnimating != view.wrapped.animates { + view.wrapped.animates = self.isAnimating + } + #else + if self.isAnimating != view.wrapped.isAnimating { + if self.isAnimating { + view.wrapped.startAnimating() + } else { + view.wrapped.stopAnimating() + } + } + #endif + configureView(view, context: context) layoutView(view, context: context) } @@ -310,30 +364,6 @@ extension AnimatedImage { } } -// Initializer -extension AnimatedImage { - public init(url: URL?, placeholder: PlatformImage? = nil, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) { - self.placeholder = placeholder - self.webOptions = options - self.webContext = context - self.imageModel.url = url - } - - public init(name: String, bundle: Bundle? = nil) { - #if os(macOS) - let image = SDAnimatedImage(named: name, in: bundle) - #else - let image = SDAnimatedImage(named: name, in: bundle, compatibleWith: nil) - #endif - self.imageModel.image = image - } - - public init(data: Data, scale: CGFloat = 0) { - let image = SDAnimatedImage(data: data, scale: scale) - self.imageModel.image = image - } -} - #if DEBUG struct AnimatedImage_Previews : PreviewProvider { static var previews: some View {