Skip to content

Feature support the Image struct method on WebImage and AnimatedImage #7

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 3 commits into from
Oct 1, 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
4 changes: 2 additions & 2 deletions Example/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -18,7 +18,7 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
SDWebImage: fb387001955223213dde14bc08c8b73f371f8d8f
SDWebImageSwiftUI: 22254f3ced4f056602cd8167b64106ab6419c6e6
SDWebImageSwiftUI: fa0b13b16a92985532cd13931b88aea4ff7efb0b

PODFILE CHECKSUM: 146734166216dd8fc1597433cc675999454ed4b2

Expand Down
2 changes: 2 additions & 0 deletions Example/SDWebImageSwiftUIDemo/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
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.

10 changes: 10 additions & 0 deletions SDWebImageSwiftUI.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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, ); }; };
Expand Down Expand Up @@ -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>"; };
Expand Down Expand Up @@ -174,6 +179,7 @@
32C43DDE22FD54C600BE87F5 /* WebImage.swift */,
32C43DDF22FD54C600BE87F5 /* AnimatedImage.swift */,
32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */,
326E480923431C0F00C633E9 /* ImageViewWrapper.swift */,
);
path = Classes;
sourceTree = "<group>";
Expand Down Expand Up @@ -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 */,
);
Expand All @@ -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 */,
);
Expand All @@ -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 */,
);
Expand All @@ -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 */,
);
Expand Down
151 changes: 132 additions & 19 deletions SDWebImageSwiftUI/Classes/AnimatedImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -31,53 +35,115 @@ 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 _ = imageLayout.aspectRatio {
// TODO: Needs layer transform and geometry calculation
}

// 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.needsLayout = true
view.needsDisplay = true
#else
view.setNeedsLayout()
view.setNeedsDisplay()
#endif
}

public func image(_ image: SDAnimatedImage?) -> Self {
Expand All @@ -90,15 +156,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 {
Expand All @@ -123,4 +223,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
12 changes: 4 additions & 8 deletions SDWebImageSwiftUI/Classes/ImageManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ class ImageManager : ObservableObject {

var objectWillChange = PassthroughSubject<ImageManager, Never>()

private var manager = SDWebImageManager.shared
private weak var currentOperation: SDWebImageOperation? = nil
var manager = SDWebImageManager.shared
weak var currentOperation: SDWebImageOperation? = nil

var image: Image? {
var image: PlatformImage? {
willSet {
objectWillChange.send(self)
}
Expand All @@ -36,11 +36,7 @@ class ImageManager : ObservableObject {
func load() {
currentOperation = manager.loadImage(with: url, options: options, context: context, progress: nil) { (image, data, error, cacheType, _, _) in
if let image = image {
#if os(macOS)
self.image = Image(nsImage: image)
#else
self.image = Image(uiImage: image)
#endif
self.image = image
}
}
}
Expand Down
Loading