Support AnimatedImage on watchOS - Using WatchKit bridge (#22)
* Temp for watchOS AnimatedImage support, using massive private API, still contains small issues * Update the hack for wacthKit experienment, fix the retain cycle issue that cause WKInterfaceImage not dealloc * Solve the merge conflict and try again * Add support for contentMode * Fix the SDAnimatedImageInterface first appear shows empty issues * Fix the scale factor support for SDAniamtedImageInterface * Fix the compile issue on other platforms * Stop animtiong when dismantle for watchOS AnimatedImage * Fix the issue that stopAnimating does not stop :) * Fix the warning because of Apple's bug * Use macro to integrate the watchOS Animation solution * Refactory code to fix that calling sd_setImage(with:) multiple times issues * Support to custom loop count on watchOS AnimatedImage * Fix the CocoaPods issues which does not have umbrella headers * Update some of the documentations * Try to solve the SwiftPM issue because it does not support mixed Objective-C and Swift code, really suck, Apple :) * Fix travis CI script to only build Carthage. Swift cli build can not works on Objective-C code import syntax, but works on Xcode
This commit is contained in:
parent
f056456f15
commit
7e21926830
|
@ -33,4 +33,5 @@ script:
|
|||
- pod install --project-directory=Example
|
||||
- xcodebuild build -workspace Example/SDWebImageSwiftUI.xcworkspace -scheme SDWebImageSwiftUIDemo -sdk iphonesimulator -destination 'name=iPhone 8' ONLY_ACTIVE_ARCH=NO | xcpretty -c
|
||||
|
||||
- swift build
|
||||
- carthage update --platform iOS
|
||||
- xcodebuild build -project SDWebImageSwiftUI.xcodeproj -scheme 'SDWebImageSwiftUI' -sdk iphonesimulator -configuration Debug | xcpretty -c
|
|
@ -1 +1 @@
|
|||
github "SDWebImage/SDWebImage" "5.1.0"
|
||||
github "SDWebImage/SDWebImage" "5.2.3"
|
||||
|
|
|
@ -11,7 +11,7 @@ PODS:
|
|||
- SDWebImage (5.2.3):
|
||||
- SDWebImage/Core (= 5.2.3)
|
||||
- SDWebImage/Core (5.2.3)
|
||||
- SDWebImageSwiftUI (0.3.2):
|
||||
- SDWebImageSwiftUI (0.3.3):
|
||||
- SDWebImage (~> 5.1)
|
||||
- SDWebImageWebPCoder (0.2.5):
|
||||
- libwebp (~> 1.0)
|
||||
|
@ -34,7 +34,7 @@ EXTERNAL SOURCES:
|
|||
SPEC CHECKSUMS:
|
||||
libwebp: 057912d6d0abfb6357d8bb05c0ea470301f5d61e
|
||||
SDWebImage: 46a7f73228f84ce80990c786e4372cf4db5875ce
|
||||
SDWebImageSwiftUI: a8a03ef596dde2e9668a76794f6c59d194289bb0
|
||||
SDWebImageSwiftUI: 2284857313ca5085ab7b5310d372420d23c0817f
|
||||
SDWebImageWebPCoder: 947093edd1349d820c40afbd9f42acb6cdecd987
|
||||
|
||||
PODFILE CHECKSUM: 3fb06a5173225e197f3a4bf2be7e5586a693257a
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -67,6 +67,9 @@ struct ContentView: View {
|
|||
Button(action: { self.reloadCache() }) {
|
||||
Text("Reload")
|
||||
}
|
||||
Button(action: { self.switchView() }) {
|
||||
Text("Switch")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -76,7 +79,6 @@ struct ContentView: View {
|
|||
ForEach(imageURLs) { url in
|
||||
NavigationLink(destination: DetailView(url: url, animated: self.animated)) {
|
||||
HStack {
|
||||
#if os(iOS) || os(tvOS) || os(macOS)
|
||||
if self.animated {
|
||||
AnimatedImage(url: URL(string:url))
|
||||
.resizable()
|
||||
|
@ -88,12 +90,6 @@ struct ContentView: View {
|
|||
.scaledToFit()
|
||||
.frame(width: CGFloat(100), height: CGFloat(100), alignment: .center)
|
||||
}
|
||||
#else
|
||||
WebImage(url: URL(string:url))
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: CGFloat(100), height: CGFloat(100), alignment: .center)
|
||||
#endif
|
||||
Text((url as NSString).lastPathComponent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ struct DetailView: View {
|
|||
contentView()
|
||||
}
|
||||
#endif
|
||||
#if os(macOS)
|
||||
#if os(macOS) || os(watchOS)
|
||||
if animated {
|
||||
contentView()
|
||||
.contextMenu {
|
||||
|
@ -45,16 +45,12 @@ struct DetailView: View {
|
|||
contentView()
|
||||
}
|
||||
#endif
|
||||
#if os(watchOS)
|
||||
contentView()
|
||||
#endif
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
func contentView() -> some View {
|
||||
HStack {
|
||||
#if os(iOS) || os(tvOS) || os(macOS)
|
||||
if animated {
|
||||
AnimatedImage(url: URL(string:url), options: [.progressiveLoad], isAnimating: $isAnimating)
|
||||
.onProgress(perform: { (receivedSize, expectedSize) in
|
||||
|
@ -79,18 +75,6 @@ struct DetailView: View {
|
|||
.resizable()
|
||||
.scaledToFit()
|
||||
}
|
||||
#else
|
||||
WebImage(url: URL(string:url), options: [.progressiveLoad])
|
||||
.onProgress(perform: { (receivedSize, expectedSize) in
|
||||
if (expectedSize >= 0) {
|
||||
self.progress = CGFloat(receivedSize) / CGFloat(expectedSize)
|
||||
} else {
|
||||
self.progress = 1
|
||||
}
|
||||
})
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,16 @@ let package = Package(
|
|||
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
|
||||
.target(
|
||||
name: "SDWebImageSwiftUI",
|
||||
dependencies: ["SDWebImage", "SDWebImageSwiftUIObjC"],
|
||||
path: "SDWebImageSwiftUI/Classes",
|
||||
exclude: ["ObjC"]
|
||||
),
|
||||
// This is implementation detail because SwiftPM does not support mixed Objective-C/Swift code, don't dependent this target
|
||||
.target(
|
||||
name: "SDWebImageSwiftUIObjC",
|
||||
dependencies: ["SDWebImage"],
|
||||
path: "SDWebImageSwiftUI/Classes"),
|
||||
path: "SDWebImageSwiftUI/Classes/ObjC",
|
||||
publicHeadersPath: "."
|
||||
)
|
||||
]
|
||||
)
|
||||
|
|
|
@ -26,7 +26,7 @@ Which aims to provide a better support for SwiftUI users.
|
|||
s.tvos.deployment_target = '13.0'
|
||||
s.watchos.deployment_target = '6.0'
|
||||
|
||||
s.source_files = 'SDWebImageSwiftUI/Classes/**/*'
|
||||
s.source_files = 'SDWebImageSwiftUI/Classes/**/*', 'SDWebImageSwiftUI/Module/*.h'
|
||||
|
||||
s.frameworks = 'SwiftUI'
|
||||
s.dependency 'SDWebImage', '~> 5.1'
|
||||
|
|
|
@ -7,6 +7,14 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
324F61C7235E07EC003973B8 /* SDAnimatedImageInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = 324F61C5235E07EC003973B8 /* SDAnimatedImageInterface.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
324F61C8235E07EC003973B8 /* SDAnimatedImageInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = 324F61C5235E07EC003973B8 /* SDAnimatedImageInterface.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
324F61C9235E07EC003973B8 /* SDAnimatedImageInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = 324F61C5235E07EC003973B8 /* SDAnimatedImageInterface.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
324F61CA235E07EC003973B8 /* SDAnimatedImageInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = 324F61C5235E07EC003973B8 /* SDAnimatedImageInterface.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
324F61CB235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 324F61C6235E07EC003973B8 /* SDAnimatedImageInterface.m */; };
|
||||
324F61CC235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 324F61C6235E07EC003973B8 /* SDAnimatedImageInterface.m */; };
|
||||
324F61CD235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 324F61C6235E07EC003973B8 /* SDAnimatedImageInterface.m */; };
|
||||
324F61CE235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 324F61C6235E07EC003973B8 /* SDAnimatedImageInterface.m */; };
|
||||
326E480A23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
|
||||
326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
|
||||
326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
|
||||
|
@ -89,6 +97,8 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
324F61C5235E07EC003973B8 /* SDAnimatedImageInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDAnimatedImageInterface.h; sourceTree = "<group>"; };
|
||||
324F61C6235E07EC003973B8 /* SDAnimatedImageInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDAnimatedImageInterface.m; sourceTree = "<group>"; };
|
||||
326E480923431C0F00C633E9 /* ImageViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewWrapper.swift; sourceTree = "<group>"; };
|
||||
32C43DCC22FD540D00BE87F5 /* SDWebImageSwiftUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImageSwiftUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
32C43DDC22FD54C600BE87F5 /* ImageManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageManager.swift; sourceTree = "<group>"; };
|
||||
|
@ -142,6 +152,15 @@
|
|||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
324F61C4235E07EC003973B8 /* ObjC */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
324F61C5235E07EC003973B8 /* SDAnimatedImageInterface.h */,
|
||||
324F61C6235E07EC003973B8 /* SDAnimatedImageInterface.m */,
|
||||
);
|
||||
path = ObjC;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32C43DC222FD540D00BE87F5 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -175,6 +194,7 @@
|
|||
32C43DDB22FD54C600BE87F5 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
324F61C4235E07EC003973B8 /* ObjC */,
|
||||
32C43DDC22FD54C600BE87F5 /* ImageManager.swift */,
|
||||
32C43DDE22FD54C600BE87F5 /* WebImage.swift */,
|
||||
32C43DDF22FD54C600BE87F5 /* AnimatedImage.swift */,
|
||||
|
@ -202,6 +222,7 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
324F61C7235E07EC003973B8 /* SDAnimatedImageInterface.h in Headers */,
|
||||
32C43DE622FD54CD00BE87F5 /* SDWebImageSwiftUI.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -210,6 +231,7 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
324F61C8235E07EC003973B8 /* SDAnimatedImageInterface.h in Headers */,
|
||||
32C43E2222FD583A00BE87F5 /* SDWebImageSwiftUI.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -218,6 +240,7 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
324F61C9235E07EC003973B8 /* SDAnimatedImageInterface.h in Headers */,
|
||||
32C43E2322FD583B00BE87F5 /* SDWebImageSwiftUI.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -226,6 +249,7 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
324F61CA235E07EC003973B8 /* SDAnimatedImageInterface.h in Headers */,
|
||||
32C43E2422FD583C00BE87F5 /* SDWebImageSwiftUI.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -320,15 +344,19 @@
|
|||
TargetAttributes = {
|
||||
32C43DCB22FD540D00BE87F5 = {
|
||||
CreatedOnToolsVersion = 11.0;
|
||||
LastSwiftMigration = 1100;
|
||||
};
|
||||
32C43DF322FD57FD00BE87F5 = {
|
||||
CreatedOnToolsVersion = 11.0;
|
||||
LastSwiftMigration = 1100;
|
||||
};
|
||||
32C43E0022FD581400BE87F5 = {
|
||||
CreatedOnToolsVersion = 11.0;
|
||||
LastSwiftMigration = 1100;
|
||||
};
|
||||
32C43E0D22FD581C00BE87F5 = {
|
||||
CreatedOnToolsVersion = 11.0;
|
||||
LastSwiftMigration = 1100;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -394,6 +422,7 @@
|
|||
326E480A23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
|
||||
32C43E1622FD583700BE87F5 /* ImageManager.swift in Sources */,
|
||||
32C43E1822FD583700BE87F5 /* AnimatedImage.swift in Sources */,
|
||||
324F61CB235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -406,6 +435,7 @@
|
|||
326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
|
||||
32C43E1922FD583700BE87F5 /* ImageManager.swift in Sources */,
|
||||
32C43E1B22FD583700BE87F5 /* AnimatedImage.swift in Sources */,
|
||||
324F61CC235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -418,6 +448,7 @@
|
|||
326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
|
||||
32C43E1C22FD583800BE87F5 /* ImageManager.swift in Sources */,
|
||||
32C43E1E22FD583800BE87F5 /* AnimatedImage.swift in Sources */,
|
||||
324F61CD235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -430,6 +461,7 @@
|
|||
326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
|
||||
32C43E1F22FD583800BE87F5 /* ImageManager.swift in Sources */,
|
||||
32C43E2122FD583800BE87F5 /* AnimatedImage.swift in Sources */,
|
||||
324F61CE235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -565,6 +597,7 @@
|
|||
32C43DD522FD540D00BE87F5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
@ -585,6 +618,7 @@
|
|||
PRODUCT_NAME = SDWebImageSwiftUI;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
|
@ -593,6 +627,7 @@
|
|||
32C43DD622FD540D00BE87F5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
@ -621,6 +656,7 @@
|
|||
32C43DFA22FD57FD00BE87F5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
|
@ -643,6 +679,7 @@
|
|||
PRODUCT_NAME = SDWebImageSwiftUI;
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -650,6 +687,7 @@
|
|||
32C43DFB22FD57FD00BE87F5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
|
@ -679,6 +717,7 @@
|
|||
32C43E0722FD581400BE87F5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
@ -699,6 +738,7 @@
|
|||
PRODUCT_NAME = SDWebImageSwiftUI;
|
||||
SDKROOT = appletvos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
};
|
||||
|
@ -707,6 +747,7 @@
|
|||
32C43E0822FD581400BE87F5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
@ -736,6 +777,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
@ -756,6 +798,7 @@
|
|||
PRODUCT_NAME = SDWebImageSwiftUI;
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
};
|
||||
|
@ -765,6 +808,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
|
|
@ -8,13 +8,13 @@
|
|||
|
||||
import SwiftUI
|
||||
import SDWebImage
|
||||
|
||||
#if !os(watchOS)
|
||||
#if canImport(SDWebImageSwiftUIObjC)
|
||||
import SDWebImageSwiftUIObjC
|
||||
#endif
|
||||
|
||||
// Data Binding Object
|
||||
final class AnimatedImageModel : ObservableObject {
|
||||
@Published var image: PlatformImage?
|
||||
@Published var url: URL?
|
||||
@Published var successBlock: ((PlatformImage, SDImageCacheType) -> Void)?
|
||||
@Published var failureBlock: ((Error) -> Void)?
|
||||
@Published var progressBlock: ((Int, Int) -> Void)?
|
||||
|
@ -38,12 +38,23 @@ final class AnimatedImageConfiguration: ObservableObject {
|
|||
@Published var customLoopCount: Int?
|
||||
}
|
||||
|
||||
// Convenient
|
||||
#if os(watchOS)
|
||||
public typealias AnimatedImageViewWrapper = SDAnimatedImageInterface
|
||||
extension SDAnimatedImageInterface {
|
||||
var wrapped: SDAnimatedImageInterface {
|
||||
return self
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// View
|
||||
public struct AnimatedImage : PlatformViewRepresentable {
|
||||
@ObservedObject var imageModel = AnimatedImageModel()
|
||||
@ObservedObject var imageLayout = AnimatedImageLayout()
|
||||
@ObservedObject var imageConfiguration = AnimatedImageConfiguration()
|
||||
|
||||
var url: URL?
|
||||
var placeholder: PlatformImage?
|
||||
var webOptions: SDWebImageOptions = []
|
||||
var webContext: [SDWebImageContextOption : Any]? = nil
|
||||
|
@ -72,7 +83,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
self.placeholder = placeholder
|
||||
self.webOptions = options
|
||||
self.webContext = context
|
||||
self.imageModel.url = url
|
||||
self.url = url
|
||||
}
|
||||
|
||||
/// Create an animated image with name and bundle.
|
||||
|
@ -90,7 +101,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
/// - Parameter isAnimating: The binding for animation control
|
||||
public init(name: String, bundle: Bundle? = nil, isAnimating: Binding<Bool>) {
|
||||
self._isAnimating = isAnimating
|
||||
#if os(macOS)
|
||||
#if os(macOS) || os(watchOS)
|
||||
let image = SDAnimatedImage(named: name, in: bundle)
|
||||
#else
|
||||
let image = SDAnimatedImage(named: name, in: bundle, compatibleWith: nil)
|
||||
|
@ -117,8 +128,10 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
|
||||
#if os(macOS)
|
||||
public typealias NSViewType = AnimatedImageViewWrapper
|
||||
#else
|
||||
#elseif os(iOS) || os(tvOS)
|
||||
public typealias UIViewType = AnimatedImageViewWrapper
|
||||
#elseif os(watchOS)
|
||||
public typealias WKInterfaceObjectType = AnimatedImageViewWrapper
|
||||
#endif
|
||||
|
||||
#if os(macOS)
|
||||
|
@ -133,7 +146,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
public static func dismantleNSView(_ nsView: AnimatedImageViewWrapper, coordinator: ()) {
|
||||
dismantleView(nsView, coordinator: coordinator)
|
||||
}
|
||||
#else
|
||||
#elseif os(iOS) || os(tvOS)
|
||||
public func makeUIView(context: UIViewRepresentableContext<AnimatedImage>) -> AnimatedImageViewWrapper {
|
||||
makeView(context: context)
|
||||
}
|
||||
|
@ -145,6 +158,18 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
public static func dismantleUIView(_ uiView: AnimatedImageViewWrapper, coordinator: ()) {
|
||||
dismantleView(uiView, coordinator: coordinator)
|
||||
}
|
||||
#elseif os(watchOS)
|
||||
public func makeWKInterfaceObject(context: WKInterfaceObjectRepresentableContext<AnimatedImage>) -> AnimatedImageViewWrapper {
|
||||
makeView(context: context)
|
||||
}
|
||||
|
||||
public func updateWKInterfaceObject(_ wkInterfaceObject: AnimatedImageViewWrapper, context: WKInterfaceObjectRepresentableContext<AnimatedImage>) {
|
||||
updateView(wkInterfaceObject, context: context)
|
||||
}
|
||||
|
||||
public static func dismantleWKInterfaceObject(_ wkInterfaceObject: AnimatedImageViewWrapper, coordinator: ()) {
|
||||
dismantleView(wkInterfaceObject, coordinator: coordinator)
|
||||
}
|
||||
#endif
|
||||
|
||||
func makeView(context: PlatformViewRepresentableContext<AnimatedImage>) -> AnimatedImageViewWrapper {
|
||||
|
@ -152,15 +177,23 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
}
|
||||
|
||||
func updateView(_ view: AnimatedImageViewWrapper, context: PlatformViewRepresentableContext<AnimatedImage>) {
|
||||
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
|
||||
self.imageModel.progressBlock?(receivedSize, expectedSize)
|
||||
}) { (image, error, cacheType, _) in
|
||||
if let image = image {
|
||||
self.imageModel.successBlock?(image, cacheType)
|
||||
} else {
|
||||
self.imageModel.failureBlock?(error ?? NSError())
|
||||
if let image = imageModel.image {
|
||||
#if os(watchOS)
|
||||
view.wrapped.setImage(image)
|
||||
#else
|
||||
view.wrapped.image = image
|
||||
#endif
|
||||
} else {
|
||||
if let url = url {
|
||||
view.wrapped.sd_setImage(with: url, placeholderImage: placeholder, options: webOptions, context: webContext, progress: { (receivedSize, expectedSize, _) in
|
||||
self.imageModel.progressBlock?(receivedSize, expectedSize)
|
||||
}) { (image, error, cacheType, _) in
|
||||
self.imageModel.image = image
|
||||
if let image = image {
|
||||
self.imageModel.successBlock?(image, cacheType)
|
||||
} else {
|
||||
self.imageModel.failureBlock?(error ?? NSError())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +202,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
if self.isAnimating != view.wrapped.animates {
|
||||
view.wrapped.animates = self.isAnimating
|
||||
}
|
||||
#else
|
||||
#elseif os(iOS) || os(tvOS)
|
||||
if self.isAnimating != view.wrapped.isAnimating {
|
||||
if self.isAnimating {
|
||||
view.wrapped.startAnimating()
|
||||
|
@ -177,6 +210,12 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
view.wrapped.stopAnimating()
|
||||
}
|
||||
}
|
||||
#elseif os(watchOS)
|
||||
if self.isAnimating {
|
||||
view.wrapped.startAnimating()
|
||||
} else {
|
||||
view.wrapped.stopAnimating()
|
||||
}
|
||||
#endif
|
||||
|
||||
configureView(view, context: context)
|
||||
|
@ -202,19 +241,24 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
case .fit:
|
||||
#if os(macOS)
|
||||
view.wrapped.imageScaling = .scaleProportionallyUpOrDown
|
||||
#else
|
||||
#elseif os(iOS) || os(tvOS)
|
||||
view.wrapped.contentMode = .scaleAspectFit
|
||||
#elseif os(watchOS)
|
||||
view.wrapped.setContentMode(.aspectFit)
|
||||
#endif
|
||||
case .fill:
|
||||
#if os(macOS)
|
||||
view.wrapped.imageScaling = .scaleAxesIndependently
|
||||
#else
|
||||
#elseif os(iOS) || os(tvOS)
|
||||
view.wrapped.contentMode = .scaleToFill
|
||||
#elseif os(watchOS)
|
||||
view.wrapped.setContentMode(.fill)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Animated Image does not support resizing mode and rendering mode
|
||||
if let image = view.wrapped.image, !image.sd_isAnimated, !image.conforms(to: SDAnimatedImageProtocol.self) {
|
||||
if let image = imageModel.image, !image.sd_isAnimated, !image.conforms(to: SDAnimatedImageProtocol.self) {
|
||||
var image = image
|
||||
// ResizingMode
|
||||
if let resizingMode = imageLayout.resizingMode {
|
||||
#if os(macOS)
|
||||
|
@ -228,14 +272,24 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
view.wrapped.image?.resizingMode = .stretch
|
||||
view.wrapped.image?.capInsets = capInsets
|
||||
#else
|
||||
view.wrapped.image = view.wrapped.image?.resizableImage(withCapInsets: capInsets, resizingMode: .stretch)
|
||||
image = image.resizableImage(withCapInsets: capInsets, resizingMode: .stretch)
|
||||
#if os(iOS) || os(tvOS)
|
||||
view.wrapped.image = image
|
||||
#elseif os(watchOS)
|
||||
view.wrapped.setImage(image)
|
||||
#endif
|
||||
#endif
|
||||
case .tile:
|
||||
#if os(macOS)
|
||||
view.wrapped.image?.resizingMode = .tile
|
||||
view.wrapped.image?.capInsets = capInsets
|
||||
#else
|
||||
view.wrapped.image = view.wrapped.image?.resizableImage(withCapInsets: capInsets, resizingMode: .tile)
|
||||
image = image.resizableImage(withCapInsets: capInsets, resizingMode: .tile)
|
||||
#if os(iOS) || os(tvOS)
|
||||
view.wrapped.image = image
|
||||
#elseif os(watchOS)
|
||||
view.wrapped.setImage(image)
|
||||
#endif
|
||||
#endif
|
||||
@unknown default:
|
||||
// Future cases, not implements
|
||||
|
@ -250,13 +304,23 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
#if os(macOS)
|
||||
view.wrapped.image?.isTemplate = true
|
||||
#else
|
||||
view.wrapped.image = view.wrapped.image?.withRenderingMode(.alwaysTemplate)
|
||||
image = image.withRenderingMode(.alwaysTemplate)
|
||||
#if os(iOS) || os(tvOS)
|
||||
view.wrapped.image = image
|
||||
#elseif os(watchOS)
|
||||
view.wrapped.setImage(image)
|
||||
#endif
|
||||
#endif
|
||||
case .original:
|
||||
#if os(macOS)
|
||||
view.wrapped.image?.isTemplate = false
|
||||
#else
|
||||
view.wrapped.image = view.wrapped.image?.withRenderingMode(.alwaysOriginal)
|
||||
image = image.withRenderingMode(.alwaysOriginal)
|
||||
#if os(iOS) || os(tvOS)
|
||||
view.wrapped.image = image
|
||||
#elseif os(watchOS)
|
||||
view.wrapped.setImage(image)
|
||||
#endif
|
||||
#endif
|
||||
@unknown default:
|
||||
// Future cases, not implements
|
||||
|
@ -265,6 +329,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
}
|
||||
}
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS)
|
||||
// Interpolation
|
||||
if let interpolation = imageLayout.interpolation {
|
||||
switch interpolation {
|
||||
|
@ -295,9 +360,11 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
view.setNeedsLayout()
|
||||
view.setNeedsDisplay()
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
func configureView(_ view: AnimatedImageViewWrapper, context: PlatformViewRepresentableContext<AnimatedImage>) {
|
||||
#if os(macOS) || os(iOS) || os(tvOS)
|
||||
// IncrementalLoad
|
||||
if let incrementalLoad = imageConfiguration.incrementalLoad {
|
||||
view.wrapped.shouldIncrementalLoad = incrementalLoad
|
||||
|
@ -319,6 +386,14 @@ public struct AnimatedImage : PlatformViewRepresentable {
|
|||
// disable custom loop count
|
||||
view.wrapped.shouldCustomLoopCount = false
|
||||
}
|
||||
#elseif os(watchOS)
|
||||
if let customLoopCount = imageConfiguration.customLoopCount {
|
||||
view.wrapped.setAnimationRepeatCount(customLoopCount as NSNumber)
|
||||
} else {
|
||||
// disable custom loop count
|
||||
view.wrapped.setAnimationRepeatCount(nil)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,9 +492,11 @@ extension AnimatedImage {
|
|||
}
|
||||
|
||||
/// Provide a max buffer size by bytes. This is used to adjust frame buffer count and can be useful when the decoding cost is expensive (such as Animated WebP software decoding). Default is nil.
|
||||
// `0` or nil means automatically adjust by calculating current memory usage.
|
||||
// `1` means without any buffer cache, each of frames will be decoded and then be freed after rendering. (Lowest Memory and Highest CPU)
|
||||
// `UInt.max` means cache all the buffer. (Lowest CPU and Highest Memory)
|
||||
///
|
||||
/// `0` or nil means automatically adjust by calculating current memory usage.
|
||||
/// `1` means without any buffer cache, each of frames will be decoded and then be freed after rendering. (Lowest Memory and Highest CPU)
|
||||
/// `UInt.max` means cache all the buffer. (Lowest CPU and Highest Memory)
|
||||
/// - Warning: watchOS does not implementes.
|
||||
/// - Parameter bufferSize: The max buffer size
|
||||
public func maxBufferSize(_ bufferSize: UInt?) -> AnimatedImage {
|
||||
imageConfiguration.maxBufferSize = bufferSize
|
||||
|
@ -429,6 +506,7 @@ extension AnimatedImage {
|
|||
/// Whehter or not to enable incremental image load for animated image. See `SDAnimatedImageView` for detailed explanation for this.
|
||||
/// - Note: If you are confused about this description, open Chrome browser to view some large GIF images with low network speed to see the animation behavior.
|
||||
/// Default is true. Set to false to only render the static poster for incremental animated image.
|
||||
/// - Warning: watchOS does not implementes.
|
||||
/// - Parameter incrementalLoad: Whether or not to incremental load
|
||||
public func incrementalLoad(_ incrementalLoad: Bool) -> AnimatedImage {
|
||||
imageConfiguration.incrementalLoad = incrementalLoad
|
||||
|
@ -479,5 +557,3 @@ struct AnimatedImage_Previews : PreviewProvider {
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* This file is part of the SDWebImage package.
|
||||
* (c) DreamPiggy <lizhuoli1126@126.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
@import SDWebImage;
|
||||
|
||||
#if SD_WATCH
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Do not use this class directly in WatchKit or Storyboard. This class is implementation detail and will be removed in the future.
|
||||
@interface SDAnimatedImageInterface : WKInterfaceImage
|
||||
|
||||
- (instancetype)init WK_AVAILABLE_WATCHOS_ONLY(6.0);
|
||||
- (void)setContentMode:(SDImageScaleMode)contentMode;
|
||||
- (void)setAnimationRepeatCount:(nullable NSNumber *)repeatCount;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
#endif
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* This file is part of the SDWebImage package.
|
||||
* (c) DreamPiggy <lizhuoli1126@126.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#import "SDAnimatedImageInterface.h"
|
||||
#if SD_WATCH
|
||||
// ImageIO.modulemap does not contains this public header
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wincomplete-umbrella"
|
||||
#import <ImageIO/CGImageAnimation.h>
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#pragma mark - SPI
|
||||
|
||||
@protocol CALayerProtocol <NSObject>
|
||||
@property (nullable, strong) id contents;
|
||||
@property CGFloat contentsScale;
|
||||
@end
|
||||
|
||||
@protocol UIViewProtocol <NSObject>
|
||||
@property (nonatomic, strong, readonly) id<CALayerProtocol> layer;
|
||||
@property (nonatomic, assign) SDImageScaleMode contentMode;
|
||||
@end
|
||||
|
||||
@interface WKInterfaceObject ()
|
||||
|
||||
// This is needed for dynamic created WKInterfaceObject, like `WKInterfaceMap`
|
||||
- (instancetype)_initForDynamicCreationWithInterfaceProperty:(NSString *)property;
|
||||
// This is remote UIView
|
||||
@property (nonatomic, strong, readonly) id<UIViewProtocol> _interfaceView;
|
||||
|
||||
@end
|
||||
|
||||
@interface SDAnimatedImageStatus : NSObject
|
||||
|
||||
@property (nonatomic, assign) BOOL shouldAnimate;
|
||||
@property (nonatomic, assign) CGImageAnimationStatus animationStatus;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SDAnimatedImageStatus
|
||||
|
||||
@end
|
||||
|
||||
@interface SDAnimatedImageInterface () {
|
||||
UIImage *_image;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong, readwrite) UIImage *currentFrame;
|
||||
@property (nonatomic, assign, readwrite) NSUInteger currentFrameIndex;
|
||||
@property (nonatomic, assign, readwrite) NSUInteger currentLoopCount;
|
||||
@property (nonatomic, assign) NSUInteger totalFrameCount;
|
||||
@property (nonatomic, assign) NSUInteger totalLoopCount;
|
||||
@property (nonatomic, strong) UIImage<SDAnimatedImage> *animatedImage;
|
||||
@property (nonatomic, assign) CGFloat animatedImageScale;
|
||||
@property (nonatomic, strong) SDAnimatedImageStatus *currentStatus;
|
||||
@property (nonatomic, strong) NSNumber *animationRepeatCount;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SDAnimatedImageInterface
|
||||
|
||||
- (instancetype)init {
|
||||
Class cls = [self class];
|
||||
NSString *UUID = [NSUUID UUID].UUIDString;
|
||||
NSString *property = [NSString stringWithFormat:@"%@_%@", cls, UUID];
|
||||
self = [self _initForDynamicCreationWithInterfaceProperty:property];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSDictionary *)interfaceDescriptionForDynamicCreation {
|
||||
// This is called by WatchKit
|
||||
return @{
|
||||
@"type" : @"image",
|
||||
@"property" : self.interfaceProperty,
|
||||
@"image" : [self.class sharedEmptyImage]
|
||||
};
|
||||
}
|
||||
|
||||
+ (UIImage *)sharedEmptyImage {
|
||||
// This is used for placeholder on `WKInterfaceImage`
|
||||
// Do not using `[UIImage new]` because WatchKit will ignore it
|
||||
static dispatch_once_t onceToken;
|
||||
static UIImage *image;
|
||||
dispatch_once(&onceToken, ^{
|
||||
UIColor *color = UIColor.clearColor;
|
||||
CGRect rect = CGRectMake(0, 0, 1, 1);
|
||||
UIGraphicsBeginImageContext(rect.size);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSetFillColorWithColor(context, [color CGColor]);
|
||||
CGContextFillRect(context, rect);
|
||||
image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
});
|
||||
return image;
|
||||
}
|
||||
|
||||
- (void)setImage:(UIImage *)image {
|
||||
if (_image == image) {
|
||||
return;
|
||||
}
|
||||
_image = image;
|
||||
|
||||
// Reset all value
|
||||
[self resetAnimatedImage];
|
||||
|
||||
[super setImage:image];
|
||||
if ([image.class conformsToProtocol:@protocol(SDAnimatedImage)]) {
|
||||
UIImage<SDAnimatedImage> *animatedImage = (UIImage<SDAnimatedImage> *)image;
|
||||
NSUInteger animatedImageFrameCount = animatedImage.animatedImageFrameCount;
|
||||
// Check the frame count
|
||||
if (animatedImageFrameCount <= 1) {
|
||||
return;
|
||||
}
|
||||
self.animatedImage = animatedImage;
|
||||
self.totalFrameCount = animatedImageFrameCount;
|
||||
// Get the current frame and loop count.
|
||||
self.totalLoopCount = self.animatedImage.animatedImageLoopCount;
|
||||
// Get the scale
|
||||
self.animatedImageScale = image.scale;
|
||||
|
||||
NSData *animatedImageData = animatedImage.animatedImageData;
|
||||
SDImageFormat format = [NSData sd_imageFormatForImageData:animatedImageData];
|
||||
if (format == SDImageFormatGIF || format == SDImageFormatPNG) {
|
||||
[self startBuiltInAnimationWithImage:animatedImage];
|
||||
}
|
||||
|
||||
// Update should animate
|
||||
[self updateShouldAnimate];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)startBuiltInAnimationWithImage:(UIImage<SDAnimatedImage> *)animatedImage {
|
||||
NSData *animatedImageData = animatedImage.animatedImageData;
|
||||
NSUInteger maxLoopCount;
|
||||
if (self.animationRepeatCount != nil) {
|
||||
maxLoopCount = self.animationRepeatCount.unsignedIntegerValue;
|
||||
} else {
|
||||
maxLoopCount = animatedImage.animatedImageLoopCount;
|
||||
}
|
||||
if (maxLoopCount == 0) {
|
||||
// The documentation says `kCFNumberPositiveInfinity may be used`, but it actually treat as 1 loop count
|
||||
// 0 was treated as 1 loop count as well, not the same as Image/IO or UIKit
|
||||
maxLoopCount = ((__bridge NSNumber *)kCFNumberPositiveInfinity).unsignedIntegerValue - 1;
|
||||
}
|
||||
NSDictionary *options = @{(__bridge NSString *)kCGImageAnimationLoopCount : @(maxLoopCount)};
|
||||
SDAnimatedImageStatus *status = [SDAnimatedImageStatus new];
|
||||
status.shouldAnimate = YES;
|
||||
__weak typeof(self) wself = self;
|
||||
status.animationStatus = CGAnimateImageDataWithBlock((__bridge CFDataRef)animatedImageData, (__bridge CFDictionaryRef)options, ^(size_t index, CGImageRef _Nonnull imageRef, bool * _Nonnull stop) {
|
||||
__strong typeof(wself) self = wself;
|
||||
if (!self) {
|
||||
*stop = YES;
|
||||
return;
|
||||
}
|
||||
if (!status.shouldAnimate) {
|
||||
*stop = YES;
|
||||
return;
|
||||
}
|
||||
// The CGImageRef provided by this API is GET only, should not call CGImageRelease
|
||||
self.currentFrame = [[UIImage alloc] initWithCGImage:imageRef scale:self.animatedImageScale orientation:UIImageOrientationUp];
|
||||
self.currentFrameIndex = index;
|
||||
// Render the frame
|
||||
[self displayLayer];
|
||||
});
|
||||
|
||||
self.currentStatus = status;
|
||||
}
|
||||
|
||||
- (void)displayLayer {
|
||||
if (self.currentFrame) {
|
||||
id<CALayerProtocol> layer = [self _interfaceView].layer;
|
||||
layer.contentsScale = self.animatedImageScale;
|
||||
layer.contents = (__bridge id)self.currentFrame.CGImage;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resetAnimatedImage
|
||||
{
|
||||
self.animatedImage = nil;
|
||||
self.totalFrameCount = 0;
|
||||
self.totalLoopCount = 0;
|
||||
// reset current state
|
||||
self.currentStatus.shouldAnimate = NO;
|
||||
self.currentStatus = nil;
|
||||
[self resetCurrentFrameIndex];
|
||||
self.animatedImageScale = 1;
|
||||
}
|
||||
|
||||
- (void)resetCurrentFrameIndex
|
||||
{
|
||||
self.currentFrame = nil;
|
||||
self.currentFrameIndex = 0;
|
||||
self.currentLoopCount = 0;
|
||||
}
|
||||
|
||||
- (void)updateShouldAnimate
|
||||
{
|
||||
self.currentStatus.shouldAnimate = self.animatedImage && self.totalFrameCount > 1;
|
||||
}
|
||||
|
||||
- (void)startAnimating {
|
||||
if (self.animatedImage) {
|
||||
self.currentStatus.shouldAnimate = YES;
|
||||
} else if (_image.images.count > 0) {
|
||||
[super startAnimating];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)startAnimatingWithImagesInRange:(NSRange)imageRange duration:(NSTimeInterval)duration repeatCount:(NSInteger)repeatCount {
|
||||
if (self.animatedImage) {
|
||||
self.currentStatus.shouldAnimate = YES;
|
||||
} else if (_image.images.count > 0) {
|
||||
[super startAnimatingWithImagesInRange:imageRange duration:duration repeatCount:repeatCount];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopAnimating {
|
||||
if (self.animatedImage) {
|
||||
self.currentStatus.shouldAnimate = NO;
|
||||
} else if (_image.images.count > 0) {
|
||||
[super stopAnimating];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setContentMode:(SDImageScaleMode)contentMode {
|
||||
[self _interfaceView].contentMode = contentMode;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - Web Cache
|
||||
|
||||
@interface SDAnimatedImageInterface (WebCache)
|
||||
|
||||
@end
|
||||
|
||||
@implementation SDAnimatedImageInterface (WebCache)
|
||||
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
placeholderImage:(nullable UIImage *)placeholder
|
||||
options:(SDWebImageOptions)options
|
||||
context:(nullable SDWebImageContext *)context
|
||||
progress:(nullable SDImageLoaderProgressBlock)progressBlock
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock {
|
||||
Class animatedImageClass = [SDAnimatedImage class];
|
||||
SDWebImageMutableContext *mutableContext;
|
||||
if (context) {
|
||||
mutableContext = [context mutableCopy];
|
||||
} else {
|
||||
mutableContext = [NSMutableDictionary dictionary];
|
||||
}
|
||||
mutableContext[SDWebImageContextAnimatedImageClass] = animatedImageClass;
|
||||
[self sd_internalSetImageWithURL:url
|
||||
placeholderImage:placeholder
|
||||
options:options
|
||||
context:mutableContext
|
||||
setImageBlock:nil
|
||||
progress:progressBlock
|
||||
completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
||||
if (completedBlock) {
|
||||
completedBlock(image, error, cacheType, imageURL);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
#endif
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SDWebImageSwiftUI/SDAnimatedImageInterface.h>
|
||||
|
||||
//! Project version number for SDWebImageSwiftUI.
|
||||
FOUNDATION_EXPORT double SDWebImageSwiftUIVersionNumber;
|
||||
|
|
Loading…
Reference in New Issue