Support AnimatedImage with `aspectRatio`, `antialiased`, `interpolation`, `renderingMode` and `resizable` methods, some of them is not fully implemented
This commit is contained in:
parent
e8939701e6
commit
611ab6125e
|
@ -2,7 +2,7 @@ PODS:
|
|||
- SDWebImage (5.1.0):
|
||||
- SDWebImage/Core (= 5.1.0)
|
||||
- SDWebImage/Core (5.1.0)
|
||||
- SDWebImageSwiftUI (0.1.0):
|
||||
- SDWebImageSwiftUI (0.1.1):
|
||||
- SDWebImage (~> 5.1)
|
||||
|
||||
DEPENDENCIES:
|
||||
|
@ -18,7 +18,7 @@ EXTERNAL SOURCES:
|
|||
|
||||
SPEC CHECKSUMS:
|
||||
SDWebImage: fb387001955223213dde14bc08c8b73f371f8d8f
|
||||
SDWebImageSwiftUI: 22254f3ced4f056602cd8167b64106ab6419c6e6
|
||||
SDWebImageSwiftUI: fa0b13b16a92985532cd13931b88aea4ff7efb0b
|
||||
|
||||
PODFILE CHECKSUM: 146734166216dd8fc1597433cc675999454ed4b2
|
||||
|
||||
|
|
|
@ -15,9 +15,11 @@ struct ContentView: View {
|
|||
var body: some View {
|
||||
VStack {
|
||||
WebImage(url: URL(string: "https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic"))
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: CGFloat(300), height: CGFloat(300), alignment: .center)
|
||||
AnimatedImage(url: URL(string: "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif"), options: [.progressiveLoad])
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.frame(width: CGFloat(400), height: CGFloat(300), alignment: .center)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
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 */; };
|
||||
326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
|
||||
32C43DE622FD54CD00BE87F5 /* SDWebImageSwiftUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 32C43DE422FD54CD00BE87F5 /* SDWebImageSwiftUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
32C43DEA22FD577300BE87F5 /* SDWebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32C43DE922FD577300BE87F5 /* SDWebImage.framework */; };
|
||||
32C43DEB22FD577300BE87F5 /* SDWebImage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 32C43DE922FD577300BE87F5 /* SDWebImage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
|
@ -85,6 +89,7 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
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>"; };
|
||||
32C43DDE22FD54C600BE87F5 /* WebImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebImage.swift; sourceTree = "<group>"; };
|
||||
|
@ -174,6 +179,7 @@
|
|||
32C43DDE22FD54C600BE87F5 /* WebImage.swift */,
|
||||
32C43DDF22FD54C600BE87F5 /* AnimatedImage.swift */,
|
||||
32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */,
|
||||
326E480923431C0F00C633E9 /* ImageViewWrapper.swift */,
|
||||
);
|
||||
path = Classes;
|
||||
sourceTree = "<group>";
|
||||
|
@ -385,6 +391,7 @@
|
|||
files = (
|
||||
32C43E1722FD583700BE87F5 /* WebImage.swift in Sources */,
|
||||
32C43E3222FD5DE100BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
|
||||
326E480A23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
|
||||
32C43E1622FD583700BE87F5 /* ImageManager.swift in Sources */,
|
||||
32C43E1822FD583700BE87F5 /* AnimatedImage.swift in Sources */,
|
||||
);
|
||||
|
@ -396,6 +403,7 @@
|
|||
files = (
|
||||
32C43E1A22FD583700BE87F5 /* WebImage.swift in Sources */,
|
||||
32C43E3322FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
|
||||
326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
|
||||
32C43E1922FD583700BE87F5 /* ImageManager.swift in Sources */,
|
||||
32C43E1B22FD583700BE87F5 /* AnimatedImage.swift in Sources */,
|
||||
);
|
||||
|
@ -407,6 +415,7 @@
|
|||
files = (
|
||||
32C43E1D22FD583800BE87F5 /* WebImage.swift in Sources */,
|
||||
32C43E3422FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
|
||||
326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
|
||||
32C43E1C22FD583800BE87F5 /* ImageManager.swift in Sources */,
|
||||
32C43E1E22FD583800BE87F5 /* AnimatedImage.swift in Sources */,
|
||||
);
|
||||
|
@ -418,6 +427,7 @@
|
|||
files = (
|
||||
32C43E2022FD583800BE87F5 /* WebImage.swift in Sources */,
|
||||
32C43E3522FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
|
||||
326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
|
||||
32C43E1F22FD583800BE87F5 /* ImageManager.swift in Sources */,
|
||||
32C43E2122FD583800BE87F5 /* AnimatedImage.swift in Sources */,
|
||||
);
|
||||
|
|
|
@ -20,6 +20,10 @@ final class AnimatedImageModel : ObservableObject {
|
|||
// Layout Binding Object
|
||||
final class AnimatedImageLayout : ObservableObject {
|
||||
@Published var contentMode: ContentMode = .fill
|
||||
@Published var aspectRatio: CGFloat?
|
||||
@Published var renderingMode: Image.TemplateRenderingMode?
|
||||
@Published var interpolation: Image.Interpolation?
|
||||
@Published var antialiased: Bool = false
|
||||
}
|
||||
|
||||
// View
|
||||
|
@ -31,53 +35,111 @@ public struct AnimatedImage : ViewRepresentable {
|
|||
var webContext: [SDWebImageContextOption : Any]? = nil
|
||||
|
||||
#if os(macOS)
|
||||
public typealias NSViewType = SDAnimatedImageView
|
||||
public typealias NSViewType = AnimatedImageViewWrapper
|
||||
#else
|
||||
public typealias UIViewType = SDAnimatedImageView
|
||||
public typealias UIViewType = AnimatedImageViewWrapper
|
||||
#endif
|
||||
|
||||
#if os(macOS)
|
||||
public func makeNSView(context: NSViewRepresentableContext<AnimatedImage>) -> SDAnimatedImageView {
|
||||
public func makeNSView(context: NSViewRepresentableContext<AnimatedImage>) -> AnimatedImageViewWrapper {
|
||||
makeView(context: context)
|
||||
}
|
||||
|
||||
public func updateNSView(_ nsView: SDAnimatedImageView, context: NSViewRepresentableContext<AnimatedImage>) {
|
||||
public func updateNSView(_ nsView: AnimatedImageViewWrapper, context: NSViewRepresentableContext<AnimatedImage>) {
|
||||
updateView(nsView, context: context)
|
||||
}
|
||||
#else
|
||||
public func makeUIView(context: UIViewRepresentableContext<AnimatedImage>) -> SDAnimatedImageView {
|
||||
public func makeUIView(context: UIViewRepresentableContext<AnimatedImage>) -> AnimatedImageViewWrapper {
|
||||
makeView(context: context)
|
||||
}
|
||||
|
||||
public func updateUIView(_ uiView: SDAnimatedImageView, context: UIViewRepresentableContext<AnimatedImage>) {
|
||||
public func updateUIView(_ uiView: AnimatedImageViewWrapper, context: UIViewRepresentableContext<AnimatedImage>) {
|
||||
updateView(uiView, context: context)
|
||||
}
|
||||
#endif
|
||||
|
||||
func makeView(context: ViewRepresentableContext<AnimatedImage>) -> SDAnimatedImageView {
|
||||
SDAnimatedImageView()
|
||||
func makeView(context: ViewRepresentableContext<AnimatedImage>) -> AnimatedImageViewWrapper {
|
||||
AnimatedImageViewWrapper()
|
||||
}
|
||||
|
||||
func updateView(_ view: SDAnimatedImageView, context: ViewRepresentableContext<AnimatedImage>) {
|
||||
view.image = imageModel.image
|
||||
func updateView(_ view: AnimatedImageViewWrapper, context: ViewRepresentableContext<AnimatedImage>) {
|
||||
view.wrapped.image = imageModel.image
|
||||
if let url = imageModel.url {
|
||||
view.sd_setImage(with: url, placeholderImage: view.image, options: webOptions, context: webContext)
|
||||
view.wrapped.sd_setImage(with: url, placeholderImage: nil, options: webOptions, context: webContext)
|
||||
}
|
||||
|
||||
layoutView(view, context: context)
|
||||
}
|
||||
|
||||
func layoutView(_ view: AnimatedImageViewWrapper, context: ViewRepresentableContext<AnimatedImage>) {
|
||||
// AspectRatio
|
||||
if let aspectRatio = imageLayout.aspectRatio {
|
||||
// Not implements
|
||||
}
|
||||
// ContentMode
|
||||
switch imageLayout.contentMode {
|
||||
case .fit:
|
||||
#if os(macOS)
|
||||
view.imageScaling = .scaleProportionallyUpOrDown
|
||||
view.wrapped.imageScaling = .scaleProportionallyUpOrDown
|
||||
#else
|
||||
view.contentMode = .scaleAspectFit
|
||||
view.wrapped.contentMode = .scaleAspectFit
|
||||
#endif
|
||||
case .fill:
|
||||
#if os(macOS)
|
||||
view.imageScaling = .scaleAxesIndependently
|
||||
view.wrapped.imageScaling = .scaleAxesIndependently
|
||||
#else
|
||||
view.contentMode = .scaleToFill
|
||||
view.wrapped.contentMode = .scaleToFill
|
||||
#endif
|
||||
}
|
||||
// RenderingMode
|
||||
if let renderingMode = imageLayout.renderingMode {
|
||||
switch renderingMode {
|
||||
case .template:
|
||||
#if os(macOS)
|
||||
view.wrapped.image?.isTemplate = true
|
||||
#else
|
||||
view.wrapped.image = view.wrapped.image?.withRenderingMode(.alwaysTemplate)
|
||||
#endif
|
||||
case .original:
|
||||
#if os(macOS)
|
||||
view.wrapped.image?.isTemplate = false
|
||||
#else
|
||||
view.wrapped.image = view.wrapped.image?.withRenderingMode(.alwaysOriginal)
|
||||
#endif
|
||||
@unknown default:
|
||||
// Future cases, not implements
|
||||
break
|
||||
}
|
||||
}
|
||||
// Interpolation
|
||||
if let interpolation = imageLayout.interpolation {
|
||||
switch interpolation {
|
||||
case .high:
|
||||
view.interpolationQuality = .high
|
||||
case .medium:
|
||||
view.interpolationQuality = .medium
|
||||
case .low:
|
||||
view.interpolationQuality = .low
|
||||
case .none:
|
||||
view.interpolationQuality = .none
|
||||
@unknown default:
|
||||
// Future cases, not implements
|
||||
break
|
||||
}
|
||||
} else {
|
||||
view.interpolationQuality = .default
|
||||
}
|
||||
// Antialiased
|
||||
view.shouldAntialias = imageLayout.antialiased
|
||||
|
||||
// Display
|
||||
#if os(macOS)
|
||||
view.updateConstraintsIfNeeded()
|
||||
view.needsDisplay = true
|
||||
#else
|
||||
view.updateConstraintsIfNeeded()
|
||||
view.setNeedsDisplay()
|
||||
#endif
|
||||
}
|
||||
|
||||
public func image(_ image: SDAnimatedImage?) -> Self {
|
||||
|
@ -90,15 +152,49 @@ public struct AnimatedImage : ViewRepresentable {
|
|||
return self
|
||||
}
|
||||
|
||||
public func scaledToFit() -> Self {
|
||||
imageLayout.contentMode = .fit
|
||||
public func resizable(
|
||||
capInsets: EdgeInsets = EdgeInsets(),
|
||||
resizingMode: Image.ResizingMode = .stretch) -> AnimatedImage
|
||||
{
|
||||
return self
|
||||
}
|
||||
|
||||
public func renderingMode(_ renderingMode: Image.TemplateRenderingMode?) -> AnimatedImage {
|
||||
imageLayout.renderingMode = renderingMode
|
||||
return self
|
||||
}
|
||||
|
||||
public func interpolation(_ interpolation: Image.Interpolation) -> AnimatedImage {
|
||||
imageLayout.interpolation = interpolation
|
||||
return self
|
||||
}
|
||||
|
||||
public func antialiased(_ isAntialiased: Bool) -> AnimatedImage {
|
||||
imageLayout.antialiased = isAntialiased
|
||||
return self
|
||||
}
|
||||
|
||||
public func scaledToFill() -> Self {
|
||||
imageLayout.contentMode = .fill
|
||||
public func aspectRatio(_ aspectRatio: CGFloat? = nil, contentMode: ContentMode) -> AnimatedImage {
|
||||
imageLayout.aspectRatio = aspectRatio
|
||||
imageLayout.contentMode = contentMode
|
||||
return self
|
||||
}
|
||||
|
||||
public func aspectRatio(_ aspectRatio: CGSize, contentMode: ContentMode) -> AnimatedImage {
|
||||
var ratio: CGFloat?
|
||||
if aspectRatio.width > 0 && aspectRatio.height > 0 {
|
||||
ratio = aspectRatio.width / aspectRatio.height
|
||||
}
|
||||
return self.aspectRatio(ratio, contentMode: contentMode)
|
||||
}
|
||||
|
||||
public func scaledToFit() -> AnimatedImage {
|
||||
self.aspectRatio(nil, contentMode: .fit)
|
||||
}
|
||||
|
||||
public func scaledToFill() -> AnimatedImage {
|
||||
self.aspectRatio(nil, contentMode: .fill)
|
||||
}
|
||||
}
|
||||
|
||||
extension AnimatedImage {
|
||||
|
@ -123,4 +219,17 @@ extension AnimatedImage {
|
|||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct AnimatedImage_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
AnimatedImage(url: URL(string: "http://assets.sbnation.com/assets/2512203/dogflops.gif"))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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 Foundation
|
||||
import SDWebImage
|
||||
|
||||
// View Wrapper
|
||||
public class AnimatedImageViewWrapper : PlatformView {
|
||||
var wrapped = SDAnimatedImageView()
|
||||
var interpolationQuality = CGInterpolationQuality.default
|
||||
var shouldAntialias = false
|
||||
|
||||
override public func draw(_ rect: CGRect) {
|
||||
#if os(macOS)
|
||||
guard let ctx = NSGraphicsContext.current?.cgContext else {
|
||||
return
|
||||
}
|
||||
#else
|
||||
guard let ctx = UIGraphicsGetCurrentContext() else {
|
||||
return
|
||||
}
|
||||
#endif
|
||||
ctx.interpolationQuality = interpolationQuality
|
||||
ctx.setShouldAntialias(shouldAntialias)
|
||||
}
|
||||
|
||||
public override init(frame frameRect: CGRect) {
|
||||
super.init(frame: frameRect)
|
||||
addSubview(wrapped)
|
||||
wrapped.bindFrameToSuperviewBounds()
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
addSubview(wrapped)
|
||||
wrapped.bindFrameToSuperviewBounds()
|
||||
}
|
||||
}
|
||||
|
||||
extension PlatformView {
|
||||
/// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
|
||||
/// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
|
||||
func bindFrameToSuperviewBounds() {
|
||||
guard let superview = self.superview else {
|
||||
print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
|
||||
return
|
||||
}
|
||||
|
||||
self.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.topAnchor.constraint(equalTo: superview.topAnchor, constant: 0).isActive = true
|
||||
self.bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: 0).isActive = true
|
||||
self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 0).isActive = true
|
||||
self.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: 0).isActive = true
|
||||
|
||||
}
|
||||
}
|
|
@ -15,6 +15,12 @@ typealias PlatformImage = NSImage
|
|||
typealias PlatformImage = UIImage
|
||||
#endif
|
||||
|
||||
#if os(macOS)
|
||||
public typealias PlatformView = NSView
|
||||
#else
|
||||
public typealias PlatformView = UIView
|
||||
#endif
|
||||
|
||||
extension Image {
|
||||
init(platformImage: PlatformImage) {
|
||||
#if os(macOS)
|
||||
|
|
|
@ -81,3 +81,17 @@ extension WebImage {
|
|||
configure { $0.antialiased(isAntialiased) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG
|
||||
struct WebImage_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
WebImage(url: URL(string: "https://raw.githubusercontent.com/SDWebImage/SDWebImage/master/SDWebImage_logo.png"))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue