Skip to content

Commit 611ab61

Browse files
committed
Support AnimatedImage with aspectRatio, antialiased, interpolation, renderingMode and resizable methods, some of them is not fully implemented
1 parent e893970 commit 611ab61

File tree

7 files changed

+223
-21
lines changed

7 files changed

+223
-21
lines changed

Example/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ PODS:
22
- SDWebImage (5.1.0):
33
- SDWebImage/Core (= 5.1.0)
44
- SDWebImage/Core (5.1.0)
5-
- SDWebImageSwiftUI (0.1.0):
5+
- SDWebImageSwiftUI (0.1.1):
66
- SDWebImage (~> 5.1)
77

88
DEPENDENCIES:
@@ -18,7 +18,7 @@ EXTERNAL SOURCES:
1818

1919
SPEC CHECKSUMS:
2020
SDWebImage: fb387001955223213dde14bc08c8b73f371f8d8f
21-
SDWebImageSwiftUI: 22254f3ced4f056602cd8167b64106ab6419c6e6
21+
SDWebImageSwiftUI: fa0b13b16a92985532cd13931b88aea4ff7efb0b
2222

2323
PODFILE CHECKSUM: 146734166216dd8fc1597433cc675999454ed4b2
2424

Example/SDWebImageSwiftUIDemo/ContentView.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ struct ContentView: View {
1515
var body: some View {
1616
VStack {
1717
WebImage(url: URL(string: "https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic"))
18+
.resizable()
1819
.scaledToFit()
1920
.frame(width: CGFloat(300), height: CGFloat(300), alignment: .center)
2021
AnimatedImage(url: URL(string: "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif"), options: [.progressiveLoad])
22+
.resizable()
2123
.scaledToFill()
2224
.frame(width: CGFloat(400), height: CGFloat(300), alignment: .center)
2325
}

SDWebImageSwiftUI.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
326E480A23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
11+
326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
12+
326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
13+
326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
1014
32C43DE622FD54CD00BE87F5 /* SDWebImageSwiftUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 32C43DE422FD54CD00BE87F5 /* SDWebImageSwiftUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
1115
32C43DEA22FD577300BE87F5 /* SDWebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32C43DE922FD577300BE87F5 /* SDWebImage.framework */; };
1216
32C43DEB22FD577300BE87F5 /* SDWebImage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 32C43DE922FD577300BE87F5 /* SDWebImage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -85,6 +89,7 @@
8589
/* End PBXCopyFilesBuildPhase section */
8690

8791
/* Begin PBXFileReference section */
92+
326E480923431C0F00C633E9 /* ImageViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewWrapper.swift; sourceTree = "<group>"; };
8893
32C43DCC22FD540D00BE87F5 /* SDWebImageSwiftUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImageSwiftUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8994
32C43DDC22FD54C600BE87F5 /* ImageManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageManager.swift; sourceTree = "<group>"; };
9095
32C43DDE22FD54C600BE87F5 /* WebImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebImage.swift; sourceTree = "<group>"; };
@@ -174,6 +179,7 @@
174179
32C43DDE22FD54C600BE87F5 /* WebImage.swift */,
175180
32C43DDF22FD54C600BE87F5 /* AnimatedImage.swift */,
176181
32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */,
182+
326E480923431C0F00C633E9 /* ImageViewWrapper.swift */,
177183
);
178184
path = Classes;
179185
sourceTree = "<group>";
@@ -385,6 +391,7 @@
385391
files = (
386392
32C43E1722FD583700BE87F5 /* WebImage.swift in Sources */,
387393
32C43E3222FD5DE100BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
394+
326E480A23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
388395
32C43E1622FD583700BE87F5 /* ImageManager.swift in Sources */,
389396
32C43E1822FD583700BE87F5 /* AnimatedImage.swift in Sources */,
390397
);
@@ -396,6 +403,7 @@
396403
files = (
397404
32C43E1A22FD583700BE87F5 /* WebImage.swift in Sources */,
398405
32C43E3322FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
406+
326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
399407
32C43E1922FD583700BE87F5 /* ImageManager.swift in Sources */,
400408
32C43E1B22FD583700BE87F5 /* AnimatedImage.swift in Sources */,
401409
);
@@ -407,6 +415,7 @@
407415
files = (
408416
32C43E1D22FD583800BE87F5 /* WebImage.swift in Sources */,
409417
32C43E3422FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
418+
326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
410419
32C43E1C22FD583800BE87F5 /* ImageManager.swift in Sources */,
411420
32C43E1E22FD583800BE87F5 /* AnimatedImage.swift in Sources */,
412421
);
@@ -418,6 +427,7 @@
418427
files = (
419428
32C43E2022FD583800BE87F5 /* WebImage.swift in Sources */,
420429
32C43E3522FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
430+
326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
421431
32C43E1F22FD583800BE87F5 /* ImageManager.swift in Sources */,
422432
32C43E2122FD583800BE87F5 /* AnimatedImage.swift in Sources */,
423433
);

SDWebImageSwiftUI/Classes/AnimatedImage.swift

Lines changed: 128 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ final class AnimatedImageModel : ObservableObject {
2020
// Layout Binding Object
2121
final class AnimatedImageLayout : ObservableObject {
2222
@Published var contentMode: ContentMode = .fill
23+
@Published var aspectRatio: CGFloat?
24+
@Published var renderingMode: Image.TemplateRenderingMode?
25+
@Published var interpolation: Image.Interpolation?
26+
@Published var antialiased: Bool = false
2327
}
2428

2529
// View
@@ -31,53 +35,111 @@ public struct AnimatedImage : ViewRepresentable {
3135
var webContext: [SDWebImageContextOption : Any]? = nil
3236

3337
#if os(macOS)
34-
public typealias NSViewType = SDAnimatedImageView
38+
public typealias NSViewType = AnimatedImageViewWrapper
3539
#else
36-
public typealias UIViewType = SDAnimatedImageView
40+
public typealias UIViewType = AnimatedImageViewWrapper
3741
#endif
3842

3943
#if os(macOS)
40-
public func makeNSView(context: NSViewRepresentableContext<AnimatedImage>) -> SDAnimatedImageView {
44+
public func makeNSView(context: NSViewRepresentableContext<AnimatedImage>) -> AnimatedImageViewWrapper {
4145
makeView(context: context)
4246
}
4347

44-
public func updateNSView(_ nsView: SDAnimatedImageView, context: NSViewRepresentableContext<AnimatedImage>) {
48+
public func updateNSView(_ nsView: AnimatedImageViewWrapper, context: NSViewRepresentableContext<AnimatedImage>) {
4549
updateView(nsView, context: context)
4650
}
4751
#else
48-
public func makeUIView(context: UIViewRepresentableContext<AnimatedImage>) -> SDAnimatedImageView {
52+
public func makeUIView(context: UIViewRepresentableContext<AnimatedImage>) -> AnimatedImageViewWrapper {
4953
makeView(context: context)
5054
}
5155

52-
public func updateUIView(_ uiView: SDAnimatedImageView, context: UIViewRepresentableContext<AnimatedImage>) {
56+
public func updateUIView(_ uiView: AnimatedImageViewWrapper, context: UIViewRepresentableContext<AnimatedImage>) {
5357
updateView(uiView, context: context)
5458
}
5559
#endif
5660

57-
func makeView(context: ViewRepresentableContext<AnimatedImage>) -> SDAnimatedImageView {
58-
SDAnimatedImageView()
61+
func makeView(context: ViewRepresentableContext<AnimatedImage>) -> AnimatedImageViewWrapper {
62+
AnimatedImageViewWrapper()
5963
}
6064

61-
func updateView(_ view: SDAnimatedImageView, context: ViewRepresentableContext<AnimatedImage>) {
62-
view.image = imageModel.image
65+
func updateView(_ view: AnimatedImageViewWrapper, context: ViewRepresentableContext<AnimatedImage>) {
66+
view.wrapped.image = imageModel.image
6367
if let url = imageModel.url {
64-
view.sd_setImage(with: url, placeholderImage: view.image, options: webOptions, context: webContext)
68+
view.wrapped.sd_setImage(with: url, placeholderImage: nil, options: webOptions, context: webContext)
6569
}
6670

71+
layoutView(view, context: context)
72+
}
73+
74+
func layoutView(_ view: AnimatedImageViewWrapper, context: ViewRepresentableContext<AnimatedImage>) {
75+
// AspectRatio
76+
if let aspectRatio = imageLayout.aspectRatio {
77+
// Not implements
78+
}
79+
// ContentMode
6780
switch imageLayout.contentMode {
6881
case .fit:
6982
#if os(macOS)
70-
view.imageScaling = .scaleProportionallyUpOrDown
83+
view.wrapped.imageScaling = .scaleProportionallyUpOrDown
7184
#else
72-
view.contentMode = .scaleAspectFit
85+
view.wrapped.contentMode = .scaleAspectFit
7386
#endif
7487
case .fill:
7588
#if os(macOS)
76-
view.imageScaling = .scaleAxesIndependently
89+
view.wrapped.imageScaling = .scaleAxesIndependently
7790
#else
78-
view.contentMode = .scaleToFill
91+
view.wrapped.contentMode = .scaleToFill
7992
#endif
8093
}
94+
// RenderingMode
95+
if let renderingMode = imageLayout.renderingMode {
96+
switch renderingMode {
97+
case .template:
98+
#if os(macOS)
99+
view.wrapped.image?.isTemplate = true
100+
#else
101+
view.wrapped.image = view.wrapped.image?.withRenderingMode(.alwaysTemplate)
102+
#endif
103+
case .original:
104+
#if os(macOS)
105+
view.wrapped.image?.isTemplate = false
106+
#else
107+
view.wrapped.image = view.wrapped.image?.withRenderingMode(.alwaysOriginal)
108+
#endif
109+
@unknown default:
110+
// Future cases, not implements
111+
break
112+
}
113+
}
114+
// Interpolation
115+
if let interpolation = imageLayout.interpolation {
116+
switch interpolation {
117+
case .high:
118+
view.interpolationQuality = .high
119+
case .medium:
120+
view.interpolationQuality = .medium
121+
case .low:
122+
view.interpolationQuality = .low
123+
case .none:
124+
view.interpolationQuality = .none
125+
@unknown default:
126+
// Future cases, not implements
127+
break
128+
}
129+
} else {
130+
view.interpolationQuality = .default
131+
}
132+
// Antialiased
133+
view.shouldAntialias = imageLayout.antialiased
134+
135+
// Display
136+
#if os(macOS)
137+
view.updateConstraintsIfNeeded()
138+
view.needsDisplay = true
139+
#else
140+
view.updateConstraintsIfNeeded()
141+
view.setNeedsDisplay()
142+
#endif
81143
}
82144

83145
public func image(_ image: SDAnimatedImage?) -> Self {
@@ -90,15 +152,49 @@ public struct AnimatedImage : ViewRepresentable {
90152
return self
91153
}
92154

93-
public func scaledToFit() -> Self {
94-
imageLayout.contentMode = .fit
155+
public func resizable(
156+
capInsets: EdgeInsets = EdgeInsets(),
157+
resizingMode: Image.ResizingMode = .stretch) -> AnimatedImage
158+
{
159+
return self
160+
}
161+
162+
public func renderingMode(_ renderingMode: Image.TemplateRenderingMode?) -> AnimatedImage {
163+
imageLayout.renderingMode = renderingMode
164+
return self
165+
}
166+
167+
public func interpolation(_ interpolation: Image.Interpolation) -> AnimatedImage {
168+
imageLayout.interpolation = interpolation
169+
return self
170+
}
171+
172+
public func antialiased(_ isAntialiased: Bool) -> AnimatedImage {
173+
imageLayout.antialiased = isAntialiased
95174
return self
96175
}
97176

98-
public func scaledToFill() -> Self {
99-
imageLayout.contentMode = .fill
177+
public func aspectRatio(_ aspectRatio: CGFloat? = nil, contentMode: ContentMode) -> AnimatedImage {
178+
imageLayout.aspectRatio = aspectRatio
179+
imageLayout.contentMode = contentMode
100180
return self
101181
}
182+
183+
public func aspectRatio(_ aspectRatio: CGSize, contentMode: ContentMode) -> AnimatedImage {
184+
var ratio: CGFloat?
185+
if aspectRatio.width > 0 && aspectRatio.height > 0 {
186+
ratio = aspectRatio.width / aspectRatio.height
187+
}
188+
return self.aspectRatio(ratio, contentMode: contentMode)
189+
}
190+
191+
public func scaledToFit() -> AnimatedImage {
192+
self.aspectRatio(nil, contentMode: .fit)
193+
}
194+
195+
public func scaledToFill() -> AnimatedImage {
196+
self.aspectRatio(nil, contentMode: .fill)
197+
}
102198
}
103199

104200
extension AnimatedImage {
@@ -123,4 +219,17 @@ extension AnimatedImage {
123219
}
124220
}
125221

222+
#if DEBUG
223+
struct AnimatedImage_Previews : PreviewProvider {
224+
static var previews: some View {
225+
Group {
226+
AnimatedImage(url: URL(string: "http://assets.sbnation.com/assets/2512203/dogflops.gif"))
227+
.resizable()
228+
.aspectRatio(contentMode: .fit)
229+
.padding()
230+
}
231+
}
232+
}
233+
#endif
234+
126235
#endif
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* This file is part of the SDWebImage package.
3+
* (c) DreamPiggy <[email protected]>
4+
*
5+
* For the full copyright and license information, please view the LICENSE
6+
* file that was distributed with this source code.
7+
*/
8+
9+
import Foundation
10+
import SDWebImage
11+
12+
// View Wrapper
13+
public class AnimatedImageViewWrapper : PlatformView {
14+
var wrapped = SDAnimatedImageView()
15+
var interpolationQuality = CGInterpolationQuality.default
16+
var shouldAntialias = false
17+
18+
override public func draw(_ rect: CGRect) {
19+
#if os(macOS)
20+
guard let ctx = NSGraphicsContext.current?.cgContext else {
21+
return
22+
}
23+
#else
24+
guard let ctx = UIGraphicsGetCurrentContext() else {
25+
return
26+
}
27+
#endif
28+
ctx.interpolationQuality = interpolationQuality
29+
ctx.setShouldAntialias(shouldAntialias)
30+
}
31+
32+
public override init(frame frameRect: CGRect) {
33+
super.init(frame: frameRect)
34+
addSubview(wrapped)
35+
wrapped.bindFrameToSuperviewBounds()
36+
}
37+
38+
public required init?(coder: NSCoder) {
39+
super.init(coder: coder)
40+
addSubview(wrapped)
41+
wrapped.bindFrameToSuperviewBounds()
42+
}
43+
}
44+
45+
extension PlatformView {
46+
/// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
47+
/// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
48+
func bindFrameToSuperviewBounds() {
49+
guard let superview = self.superview else {
50+
print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
51+
return
52+
}
53+
54+
self.translatesAutoresizingMaskIntoConstraints = false
55+
self.topAnchor.constraint(equalTo: superview.topAnchor, constant: 0).isActive = true
56+
self.bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: 0).isActive = true
57+
self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 0).isActive = true
58+
self.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: 0).isActive = true
59+
60+
}
61+
}

SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ typealias PlatformImage = NSImage
1515
typealias PlatformImage = UIImage
1616
#endif
1717

18+
#if os(macOS)
19+
public typealias PlatformView = NSView
20+
#else
21+
public typealias PlatformView = UIView
22+
#endif
23+
1824
extension Image {
1925
init(platformImage: PlatformImage) {
2026
#if os(macOS)

SDWebImageSwiftUI/Classes/WebImage.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,17 @@ extension WebImage {
8181
configure { $0.antialiased(isAntialiased) }
8282
}
8383
}
84+
85+
86+
#if DEBUG
87+
struct WebImage_Previews : PreviewProvider {
88+
static var previews: some View {
89+
Group {
90+
WebImage(url: URL(string: "https://raw.githubusercontent.com/SDWebImage/SDWebImage/master/SDWebImage_logo.png"))
91+
.resizable()
92+
.aspectRatio(contentMode: .fit)
93+
.padding()
94+
}
95+
}
96+
}
97+
#endif

0 commit comments

Comments
 (0)