Skip to content

Refactory the watchOS Animation solution using the SDAnimatedImagePlayer (~> 5.3.0) #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Nov 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "SDWebImage/SDWebImage" ~> 5.1
github "SDWebImage/SDWebImage" ~> 5.3
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "SDWebImage/SDWebImage" "5.2.3"
github "SDWebImage/SDWebImage" "5.3.0"
14 changes: 7 additions & 7 deletions Example/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ PODS:
- libwebp/mux (1.0.3):
- libwebp/demux
- libwebp/webp (1.0.3)
- SDWebImage (5.2.5):
- SDWebImage/Core (= 5.2.5)
- SDWebImage/Core (5.2.5)
- SDWebImageSwiftUI (0.6.2):
- SDWebImage (~> 5.1)
- SDWebImage (5.3.0):
- SDWebImage/Core (= 5.3.0)
- SDWebImage/Core (5.3.0)
- SDWebImageSwiftUI (0.7.0):
- SDWebImage (~> 5.3)
- SDWebImageWebPCoder (0.2.5):
- libwebp (~> 1.0)
- SDWebImage/Core (~> 5.0)
Expand All @@ -33,8 +33,8 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
libwebp: 057912d6d0abfb6357d8bb05c0ea470301f5d61e
SDWebImage: 4eabf2fa6695c95c47724214417a9c036c965e4a
SDWebImageSwiftUI: 26a485e004cd64f5dde194bd97882aba962ab5b0
SDWebImage: f1afa74b86587c2c63f4e80dfd39b21e3c17b45b
SDWebImageSwiftUI: 72add2eb640bdadc856cdc30954528bffe8dfec0
SDWebImageWebPCoder: 947093edd1349d820c40afbd9f42acb6cdecd987

PODFILE CHECKSUM: 3fb06a5173225e197f3a4bf2be7e5586a693257a
Expand Down
4 changes: 2 additions & 2 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ let package = Package(
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.1.0")
.package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.3.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
2 changes: 1 addition & 1 deletion SDWebImageSwiftUI.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ Which aims to provide a better support for SwiftUI users.
s.source_files = 'SDWebImageSwiftUI/Classes/**/*', 'SDWebImageSwiftUI/Module/*.h'

s.frameworks = 'SwiftUI'
s.dependency 'SDWebImage', '~> 5.1'
s.dependency 'SDWebImage', '~> 5.3'
s.swift_version = '5.1'
end
117 changes: 88 additions & 29 deletions SDWebImageSwiftUI/Classes/AnimatedImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ import SDWebImage
import SDWebImageSwiftUIObjC
#endif

// Data Binding Object
final class AnimatedImageModel : ObservableObject {
@Published var image: PlatformImage?
}

// Convenient
#if os(watchOS)
public typealias AnimatedImageViewWrapper = SDAnimatedImageInterface
Expand All @@ -39,10 +34,9 @@ public final class AnimatedImageCoordinator: NSObject {

// View
public struct AnimatedImage : PlatformViewRepresentable {
@ObservedObject var imageModel = AnimatedImageModel()

// Options
var url: URL?
@State var image: PlatformImage?
var webOptions: SDWebImageOptions = []
var webContext: [SDWebImageContextOption : Any]? = nil

Expand All @@ -64,6 +58,10 @@ public struct AnimatedImage : PlatformViewRepresentable {
var incrementalLoad: Bool?
var maxBufferSize: UInt?
var customLoopCount: Int?
var runLoopMode: RunLoop.Mode?
var pausable: Bool?
var purgeable: Bool?
var playBackRate: Double?
#if os(macOS) || os(iOS) || os(tvOS)
// These configurations only useful for web image loading
var indicator: SDWebImageIndicator?
Expand All @@ -80,11 +78,6 @@ public struct AnimatedImage : PlatformViewRepresentable {
/// True to start animation, false to stop animation.
@Binding public var isAnimating: Bool

/// Current loaded image, may be `SDAnimatedImage` type
public var image: PlatformImage? {
imageModel.image
}

/// Create an animated image with url, placeholder, custom options and context.
/// - Parameter url: The image url
/// - Parameter placeholder: The placeholder image to show during loading
Expand Down Expand Up @@ -127,7 +120,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
#else
let image = SDAnimatedImage(named: name, in: bundle, compatibleWith: nil)
#endif
self.imageModel.image = image
self.image = image
}

/// Create an animated image with data and scale.
Expand All @@ -144,7 +137,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
public init(data: Data, scale: CGFloat = 0, isAnimating: Binding<Bool>) {
self._isAnimating = isAnimating
let image = SDAnimatedImage(data: data, scale: scale)
self.imageModel.image = image
self.image = image
}

#if os(macOS)
Expand Down Expand Up @@ -208,8 +201,10 @@ public struct AnimatedImage : PlatformViewRepresentable {
view.wrapped.sd_setImage(with: url, placeholderImage: placeholder, options: webOptions, context: webContext, progress: { (receivedSize, expectedSize, _) in
self.progressBlock?(receivedSize, expectedSize)
}) { (image, error, cacheType, _) in
DispatchQueue.main.async {
self.image = image
}
if let image = image {
self.imageModel.image = image
self.successBlock?(image, cacheType)
} else {
self.failureBlock?(error ?? NSError())
Expand All @@ -226,12 +221,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
}

func updateView(_ view: AnimatedImageViewWrapper, context: Context) {
// macOS SDAnimatedImageView.animates should initialize to true in advance before set image
#if os(macOS)
view.wrapped.animates = true
#endif

if let image = self.imageModel.image {
if let image = self.image {
#if os(watchOS)
view.wrapped.setImage(image)
#else
Expand All @@ -248,7 +238,9 @@ public struct AnimatedImage : PlatformViewRepresentable {
}

#if os(macOS)
view.wrapped.animates = self.isAnimating
if self.isAnimating != view.wrapped.animates {
view.wrapped.animates = self.isAnimating
}
#else
if self.isAnimating != view.wrapped.isAnimating {
if self.isAnimating {
Expand Down Expand Up @@ -338,17 +330,15 @@ public struct AnimatedImage : PlatformViewRepresentable {

#if os(macOS)
view.wrapped.imageScaling = contentMode
#elseif os(iOS) || os(tvOS)
#else
view.wrapped.contentMode = contentMode
#elseif os(watchOS)
view.wrapped.setContentMode(contentMode)
#endif

// Animated Image does not support resizing mode and rendering mode
if let image = self.imageModel.image, !image.sd_isAnimated, !image.conforms(to: SDAnimatedImageProtocol.self) {
if let image = self.image, !image.sd_isAnimated, !image.conforms(to: SDAnimatedImageProtocol.self) {
var image = image
// ResizingMode
if let resizingMode = self.resizingMode {
if let resizingMode = self.resizingMode, capInsets != EdgeInsets() {
#if os(macOS)
let capInsets = NSEdgeInsets(top: self.capInsets.top, left: self.capInsets.leading, bottom: self.capInsets.bottom, right: self.capInsets.trailing)
#else
Expand Down Expand Up @@ -467,12 +457,40 @@ public struct AnimatedImage : PlatformViewRepresentable {
}
#elseif os(watchOS)
if let customLoopCount = self.customLoopCount {
view.wrapped.setAnimationRepeatCount(customLoopCount as NSNumber)
view.wrapped.animationRepeatCount = customLoopCount as NSNumber
} else {
// disable custom loop count
view.wrapped.setAnimationRepeatCount(nil)
view.wrapped.animationRepeatCount = nil
}
#endif

// RunLoop Mode
if let runLoopMode = self.runLoopMode {
view.wrapped.runLoopMode = runLoopMode
} else {
view.wrapped.runLoopMode = .common
}

// Pausable
if let pausable = self.pausable {
view.wrapped.resetFrameIndexWhenStopped = !pausable
} else {
view.wrapped.resetFrameIndexWhenStopped = false
}

// Clear Buffer
if let purgeable = self.purgeable {
view.wrapped.clearBufferWhenStopped = purgeable
} else {
view.wrapped.clearBufferWhenStopped = false
}

// Playback Rate
if let playBackRate = self.playBackRate {
view.wrapped.playbackRate = playBackRate
} else {
view.wrapped.playbackRate = 1.0
}
}
}

Expand Down Expand Up @@ -625,6 +643,47 @@ extension AnimatedImage {
result.incrementalLoad = incrementalLoad
return result
}

/// The runLoopMode when animation is playing on. Defaults is `.common`
/// You can specify a runloop mode to let it rendering.
/// - Note: This is useful for some cases, for example, always specify NSDefaultRunLoopMode, if you want to pause the animation when user scroll (for Mac user, drag the mouse or touchpad)
/// - Parameter runLoopMode: The runLoopMode for animation
public func runLoopMode(_ runLoopMode: RunLoop.Mode) -> AnimatedImage {
var result = self
result.runLoopMode = runLoopMode
return result
}

/// Whether or not to pause the animation (keep current frame), instead of stop the animation (frame index reset to 0). When `isAnimating` binding value changed to false. Defaults is true.
/// - Note: For some of use case, you may want to reset the frame index to 0 when stop, but some other want to keep the current frame index.
/// - Parameter pausable: Whether or not to pause the animation instead of stop the animation.
public func pausable(_ pausable: Bool) -> AnimatedImage {
var result = self
result.pausable = pausable
return result
}

/// Whether or not to clear frame buffer cache when stopped. Defaults is false.
/// Note: This is useful when you want to limit the memory usage during frequently visibility changes (such as image view inside a list view, then push and pop)
/// - Parameter purgeable: Whether or not to clear frame buffer cache when stopped.
public func purgeable(_ purgeable: Bool) -> AnimatedImage {
var result = self
result.purgeable = purgeable
return result
}

/// Control the animation playback rate. Default is 1.0.
/// `1.0` means the normal speed.
/// `0.0` means stopping the animation.
/// `0.0-1.0` means the slow speed.
/// `> 1.0` means the fast speed.
/// `< 0.0` is not supported currently and stop animation. (may support reverse playback in the future)
/// - Parameter playBackRate: The animation playback rate.
public func playBackRate(_ playBackRate: Double) -> AnimatedImage {
var result = self
result.playBackRate = playBackRate
return result
}
}

// Completion Handler
Expand Down
10 changes: 8 additions & 2 deletions SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,16 @@ NS_ASSUME_NONNULL_BEGIN
@interface SDAnimatedImageInterface : WKInterfaceImage

@property (nonatomic, assign, getter=isAnimating, readonly) BOOL animating;
@property (nonatomic, assign) SDImageScaleMode contentMode;
@property (nonatomic, strong, nullable) NSNumber *animationRepeatCount;
@property (nonatomic, copy) NSRunLoopMode runLoopMode;
@property (nonatomic, assign) BOOL resetFrameIndexWhenStopped;
@property (nonatomic, assign) BOOL clearBufferWhenStopped;
@property (nonatomic, assign) double playbackRate;

- (instancetype)init WK_AVAILABLE_WATCHOS_ONLY(6.0);
- (void)setContentMode:(SDImageScaleMode)contentMode;
- (void)setAnimationRepeatCount:(nullable NSNumber *)repeatCount;

/// Trigger the animation check when view appears/disappears
- (void)updateAnimation;

@end
Expand Down
Loading