diff --git a/README.md b/README.md index 2ef393de..a4d8e1de 100644 --- a/README.md +++ b/README.md @@ -61,11 +61,10 @@ let package = Package( ## Usage -+ Using `WebImage` to load network image +### Using `WebImage` to load network image -Supports the placeholder and detail options control for image loading as SDWebImage. - -Supports the success/failure/progress changes event for custom handling. +- [x] Supports the placeholder and detail options control for image loading as SDWebImage. +- [x] Supports the success/failure/progress changes event for custom handling. Note: Unlike `UIImageView` in UIKit, SwiftUI's `Image` does not support animation. This `WebImage` using `Image` for internal implementation and supports static image format only. @@ -81,20 +80,25 @@ var body: some View { } ``` -+ Using `AnimatedImage` to play animation +### Using `AnimatedImage` to play animation ```swift var body: some View { - 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"))) - .scaledToFill() + Group { + 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"))) + .customLoopCount(1) + } } ``` +- [x] Supports network image as well as local data and bundle image +- [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. ## Demo diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 60fc361e..89c3442b 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -31,10 +31,18 @@ final class AnimatedImageLayout : ObservableObject { @Published var antialiased: Bool = false } +// Configuration Binding Object +final class AnimatedImageConfiguration: ObservableObject { + @Published var incrementalLoad: Bool? + @Published var maxBufferSize: UInt? + @Published var customLoopCount: Int? +} + // View -public struct AnimatedImage : ViewRepresentable { +public struct AnimatedImage : PlatformViewRepresentable { @ObservedObject var imageModel = AnimatedImageModel() @ObservedObject var imageLayout = AnimatedImageLayout() + @ObservedObject var imageConfiguration = AnimatedImageConfiguration() var placeholder: PlatformImage? var webOptions: SDWebImageOptions = [] @@ -64,11 +72,11 @@ public struct AnimatedImage : ViewRepresentable { } #endif - func makeView(context: ViewRepresentableContext) -> AnimatedImageViewWrapper { + func makeView(context: PlatformViewRepresentableContext) -> AnimatedImageViewWrapper { AnimatedImageViewWrapper() } - func updateView(_ view: AnimatedImageViewWrapper, context: ViewRepresentableContext) { + func updateView(_ view: AnimatedImageViewWrapper, context: PlatformViewRepresentableContext) { view.wrapped.image = imageModel.image if let url = imageModel.url { view.wrapped.sd_setImage(with: url, placeholderImage: placeholder, options: webOptions, context: webContext, progress: { (receivedSize, expectedSize, _) in @@ -82,10 +90,11 @@ public struct AnimatedImage : ViewRepresentable { } } + configureView(view, context: context) layoutView(view, context: context) } - func layoutView(_ view: AnimatedImageViewWrapper, context: ViewRepresentableContext) { + func layoutView(_ view: AnimatedImageViewWrapper, context: PlatformViewRepresentableContext) { // AspectRatio if let _ = imageLayout.aspectRatio { // TODO: Needs layer transform and geometry calculation @@ -190,6 +199,30 @@ public struct AnimatedImage : ViewRepresentable { view.setNeedsDisplay() #endif } + + func configureView(_ view: AnimatedImageViewWrapper, context: PlatformViewRepresentableContext) { + // IncrementalLoad + if let incrementalLoad = imageConfiguration.incrementalLoad { + view.wrapped.shouldIncrementalLoad = incrementalLoad + } + + // MaxBufferSize + if let maxBufferSize = imageConfiguration.maxBufferSize { + view.wrapped.maxBufferSize = maxBufferSize + } else { + // automatically + view.wrapped.maxBufferSize = 0 + } + + // CustomLoopCount + if let customLoopCount = imageConfiguration.customLoopCount { + view.wrapped.shouldCustomLoopCount = true + view.wrapped.animationRepeatCount = customLoopCount + } else { + // disable custom loop count + view.wrapped.shouldCustomLoopCount = false + } + } } // Layout @@ -241,6 +274,24 @@ extension AnimatedImage { } } +// AnimatedImage Modifier +extension AnimatedImage { + public func customLoopCount(_ loopCount: Int?) -> AnimatedImage { + imageConfiguration.customLoopCount = loopCount + return self + } + + public func maxBufferSize(_ bufferSize: UInt?) -> AnimatedImage { + imageConfiguration.maxBufferSize = bufferSize + return self + } + + public func incrementalLoad(_ incrementalLoad: Bool) -> AnimatedImage { + imageConfiguration.incrementalLoad = incrementalLoad + return self + } +} + // Completion Handler extension AnimatedImage { public func onFailure(perform action: ((Error) -> Void)? = nil) -> AnimatedImage { diff --git a/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift b/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift index 35ff4756..1f434f24 100644 --- a/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift +++ b/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift @@ -36,14 +36,14 @@ public typealias PlatformView = WKInterfaceObject #endif #if os(macOS) -typealias ViewRepresentable = NSViewRepresentable -typealias ViewRepresentableContext = NSViewRepresentableContext +typealias PlatformViewRepresentable = NSViewRepresentable +typealias PlatformViewRepresentableContext = NSViewRepresentableContext #endif #if os(iOS) || os(tvOS) -typealias ViewRepresentable = UIViewRepresentable -typealias ViewRepresentableContext = UIViewRepresentableContext +typealias PlatformViewRepresentable = UIViewRepresentable +typealias PlatformViewRepresentableContext = UIViewRepresentableContext #endif #if os(watchOS) -typealias ViewRepresentable = WKInterfaceObjectRepresentable -typealias ViewRepresentableContext = WKInterfaceObjectRepresentableContext +typealias PlatformViewRepresentable = WKInterfaceObjectRepresentable +typealias PlatformViewRepresentableContext = WKInterfaceObjectRepresentableContext #endif