diff --git a/SDWebImageSwiftUI/Classes/ImagePlayer.swift b/SDWebImageSwiftUI/Classes/ImagePlayer.swift index 90978df0..9c1e41e0 100644 --- a/SDWebImageSwiftUI/Classes/ImagePlayer.swift +++ b/SDWebImageSwiftUI/Classes/ImagePlayer.swift @@ -37,6 +37,22 @@ public final class ImagePlayer : ObservableObject { /// Current playing frame image @Published public var currentFrame: PlatformImage? + /// Current playing frame index + @Published public var currentFrameIndex: UInt = 0 + + /// Current playing loop count + @Published public var currentLoopCount: UInt = 0 + + /// Whether current player is valid for playing. This will check the internal player exist or not + public var isValid: Bool { + player != nil + } + + /// Current playing status + public var isPlaying: Bool { + player?.isPlaying ?? false + } + /// Start the animation public func startPlaying() { player?.startPlaying() @@ -52,38 +68,43 @@ public final class ImagePlayer : ObservableObject { player?.stopPlaying() } + /// Seek to frame and loop count + public func seekToFrame(at: UInt, loopCount: UInt) { + player?.seekToFrame(at: at, loopCount: loopCount) + } + /// Clear the frame buffer public func clearFrameBuffer() { player?.clearFrameBuffer() } - - /// Setup the player using Animated Image + /// Setup the player using Animated Image. + /// After setup, you can always check `isValid` status, or call `startPlaying` to play the animation. /// - Parameter image: animated image - public func setupPlayer(image: PlatformImage?) { - if player != nil { + public func setupPlayer(animatedImage: SDAnimatedImageProvider) { + if isValid { return } - if let animatedImage = image as? SDAnimatedImageProvider & PlatformImage { - if let imagePlayer = SDAnimatedImagePlayer(provider: animatedImage) { - imagePlayer.animationFrameHandler = { [weak self] (_, frame) in - self?.currentFrame = frame - } - // Setup configuration - if let maxBufferSize = maxBufferSize { - imagePlayer.maxBufferSize = maxBufferSize - } - if let customLoopCount = customLoopCount { - imagePlayer.totalLoopCount = customLoopCount - } - imagePlayer.runLoopMode = runLoopMode - imagePlayer.playbackRate = playbackRate - imagePlayer.playbackMode = playbackMode - - self.player = imagePlayer - - imagePlayer.startPlaying() + if let imagePlayer = SDAnimatedImagePlayer(provider: animatedImage) { + imagePlayer.animationFrameHandler = { [weak self] (index, frame) in + self?.currentFrameIndex = index + self?.currentFrame = frame + } + imagePlayer.animationLoopHandler = { [weak self] (loopCount) in + self?.currentLoopCount = loopCount + } + // Setup configuration + if let maxBufferSize = maxBufferSize { + imagePlayer.maxBufferSize = maxBufferSize + } + if let customLoopCount = customLoopCount { + imagePlayer.totalLoopCount = customLoopCount } + imagePlayer.runLoopMode = runLoopMode + imagePlayer.playbackRate = playbackRate + imagePlayer.playbackMode = playbackMode + + self.player = imagePlayer } } } diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index f6d65f4f..6e0c3f34 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -17,6 +17,8 @@ public struct WebImage : View { var placeholder: AnyView? var retryOnAppear: Bool = true var cancelOnDisappear: Bool = true + var pausable: Bool = true + var purgeable: Bool = false @ObservedObject var imageManager: ImageManager @@ -26,9 +28,6 @@ public struct WebImage : View { @ObservedObject var imagePlayer: ImagePlayer - var pausable: Bool = true - var purgeable: Bool = false - /// Create a web image with url, placeholder, custom options and context. /// - Parameter url: The image url /// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values. @@ -61,38 +60,30 @@ public struct WebImage : View { imageManager.load() } return Group { - if imageManager.image != nil { + if let image = imageManager.image { if isAnimating && !imageManager.isIncremental { - if imagePlayer.currentFrame != nil { - configure(image: imagePlayer.currentFrame!) - .onPlatformAppear(appear: { - self.imagePlayer.startPlaying() - }, disappear: { - if self.pausable { - self.imagePlayer.pausePlaying() - } else { - self.imagePlayer.stopPlaying() - } - if self.purgeable { - self.imagePlayer.clearFrameBuffer() - } - }) - } else { - configure(image: imageManager.image!) - .onReceive(imageManager.$image) { image in - self.imagePlayer.setupPlayer(image: image) + setupPlayer() + .onPlatformAppear(appear: { + self.imagePlayer.startPlaying() + }, disappear: { + if self.pausable { + self.imagePlayer.pausePlaying() + } else { + self.imagePlayer.stopPlaying() } - } + if self.purgeable { + self.imagePlayer.clearFrameBuffer() + } + }) } else { - if imagePlayer.currentFrame != nil { - configure(image: imagePlayer.currentFrame!) + if let currentFrame = imagePlayer.currentFrame { + configure(image: currentFrame) } else { - configure(image: imageManager.image!) + configure(image: image) } } } else { setupPlaceholder() - .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) .onPlatformAppear(appear: { // Load remote image when first appear if self.imageManager.isFirstLoad { @@ -164,6 +155,19 @@ public struct WebImage : View { } } + /// Animated Image Support + func setupPlayer() -> some View { + if let currentFrame = imagePlayer.currentFrame { + return configure(image: currentFrame) + } else { + if let animatedImage = imageManager.image as? SDAnimatedImageProvider { + self.imagePlayer.setupPlayer(animatedImage: animatedImage) + self.imagePlayer.startPlaying() + } + return configure(image: imageManager.image!) + } + } + /// Placeholder View Support func setupPlaceholder() -> some View { // Don't use `Group` because it will trigger `.onAppear` and `.onDisappear` when condition view removed, treat placeholder as an entire component