Merge pull request #13 from SDWebImage/feature_animatedimage_modifier

Feature animatedimage modifier
This commit is contained in:
DreamPiggy 2019-10-05 15:06:28 +08:00 committed by GitHub
commit 840b6725b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 22 deletions

View File

@ -61,11 +61,10 @@ let package = Package(
## Usage ## 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. - [x] Supports the placeholder and detail options control for image loading as SDWebImage.
- [x] Supports the success/failure/progress changes event for custom handling.
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. 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 ```swift
var body: some View { var body: some View {
AnimatedImage(url: URL(string: "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif")) Group {
.onFailure(perform: { (error) in AnimatedImage(url: URL(string: "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif"))
// Error .onFailure(perform: { (error) in
}) // Error
.scaledToFit() })
AnimatedImage(data: try! Data(contentsOf: URL(fileURLWithPath: "/tmp/foo.webp"))) .scaledToFit()
.scaledToFill() 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. 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 ## Demo

View File

@ -31,10 +31,18 @@ final class AnimatedImageLayout : ObservableObject {
@Published var antialiased: Bool = false @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 // View
public struct AnimatedImage : ViewRepresentable { public struct AnimatedImage : PlatformViewRepresentable {
@ObservedObject var imageModel = AnimatedImageModel() @ObservedObject var imageModel = AnimatedImageModel()
@ObservedObject var imageLayout = AnimatedImageLayout() @ObservedObject var imageLayout = AnimatedImageLayout()
@ObservedObject var imageConfiguration = AnimatedImageConfiguration()
var placeholder: PlatformImage? var placeholder: PlatformImage?
var webOptions: SDWebImageOptions = [] var webOptions: SDWebImageOptions = []
@ -64,11 +72,11 @@ public struct AnimatedImage : ViewRepresentable {
} }
#endif #endif
func makeView(context: ViewRepresentableContext<AnimatedImage>) -> AnimatedImageViewWrapper { func makeView(context: PlatformViewRepresentableContext<AnimatedImage>) -> AnimatedImageViewWrapper {
AnimatedImageViewWrapper() AnimatedImageViewWrapper()
} }
func updateView(_ view: AnimatedImageViewWrapper, context: ViewRepresentableContext<AnimatedImage>) { func updateView(_ view: AnimatedImageViewWrapper, context: PlatformViewRepresentableContext<AnimatedImage>) {
view.wrapped.image = imageModel.image view.wrapped.image = imageModel.image
if let url = imageModel.url { if let url = imageModel.url {
view.wrapped.sd_setImage(with: url, placeholderImage: placeholder, options: webOptions, context: webContext, progress: { (receivedSize, expectedSize, _) in 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) layoutView(view, context: context)
} }
func layoutView(_ view: AnimatedImageViewWrapper, context: ViewRepresentableContext<AnimatedImage>) { func layoutView(_ view: AnimatedImageViewWrapper, context: PlatformViewRepresentableContext<AnimatedImage>) {
// AspectRatio // AspectRatio
if let _ = imageLayout.aspectRatio { if let _ = imageLayout.aspectRatio {
// TODO: Needs layer transform and geometry calculation // TODO: Needs layer transform and geometry calculation
@ -190,6 +199,30 @@ public struct AnimatedImage : ViewRepresentable {
view.setNeedsDisplay() view.setNeedsDisplay()
#endif #endif
} }
func configureView(_ view: AnimatedImageViewWrapper, context: PlatformViewRepresentableContext<AnimatedImage>) {
// 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 // 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 // Completion Handler
extension AnimatedImage { extension AnimatedImage {
public func onFailure(perform action: ((Error) -> Void)? = nil) -> AnimatedImage { public func onFailure(perform action: ((Error) -> Void)? = nil) -> AnimatedImage {

View File

@ -36,14 +36,14 @@ public typealias PlatformView = WKInterfaceObject
#endif #endif
#if os(macOS) #if os(macOS)
typealias ViewRepresentable = NSViewRepresentable typealias PlatformViewRepresentable = NSViewRepresentable
typealias ViewRepresentableContext = NSViewRepresentableContext typealias PlatformViewRepresentableContext = NSViewRepresentableContext
#endif #endif
#if os(iOS) || os(tvOS) #if os(iOS) || os(tvOS)
typealias ViewRepresentable = UIViewRepresentable typealias PlatformViewRepresentable = UIViewRepresentable
typealias ViewRepresentableContext = UIViewRepresentableContext typealias PlatformViewRepresentableContext = UIViewRepresentableContext
#endif #endif
#if os(watchOS) #if os(watchOS)
typealias ViewRepresentable = WKInterfaceObjectRepresentable typealias PlatformViewRepresentable = WKInterfaceObjectRepresentable
typealias ViewRepresentableContext = WKInterfaceObjectRepresentableContext typealias PlatformViewRepresentableContext = WKInterfaceObjectRepresentableContext
#endif #endif