From e1c32aea7d21e2a1c997097f95af95eeef1b920a Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Tue, 20 Sep 2022 20:26:35 +0800 Subject: [PATCH 01/90] Fix iOS 13 compatibility Revert back the onPlatformAppear to fix iOS 14+ behavior Use backport for all OSs --- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 16 +--- SDWebImageSwiftUI/Classes/ImageManager.swift | 29 ++----- .../Classes/SwiftUICompatibility.swift | 84 +++++++++++++++++++ SDWebImageSwiftUI/Classes/WebImage.swift | 55 ++++++------ Tests/ImageManagerTests.swift | 4 +- 5 files changed, 122 insertions(+), 66 deletions(-) create mode 100644 SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index acddf186..e454432f 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -101,15 +101,7 @@ final class AnimatedImageConfiguration: ObservableObject { /// A Image View type to load image from url, data or bundle. Supports animated and static image format. @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) public struct AnimatedImage : PlatformViewRepresentable { - @SwiftUI.StateObject var imageModel_SwiftUI = AnimatedImageModel() - @Backport.StateObject var imageModel_Backport = AnimatedImageModel() - var imageModel: AnimatedImageModel { - if #available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) { - return imageModel_SwiftUI - } else { - return imageModel_Backport - } - } + @ObservedObject var imageModel: AnimatedImageModel @ObservedObject var imageHandler = AnimatedImageHandler() @ObservedObject var imageLayout = AnimatedImageLayout() @ObservedObject var imageConfiguration = AnimatedImageConfiguration() @@ -186,11 +178,7 @@ public struct AnimatedImage : PlatformViewRepresentable { init(imageModel: AnimatedImageModel, isAnimating: Binding) { self._isAnimating = isAnimating - if #available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) { - _imageModel_SwiftUI = SwiftUI.StateObject(wrappedValue: imageModel) - } else { - _imageModel_Backport = Backport.StateObject(wrappedValue: imageModel) - } + _imageModel = ObservedObject(wrappedValue: imageModel) } #if os(macOS) diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index 202f7ccf..94ce96e4 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -28,37 +28,24 @@ public final class ImageManager : ObservableObject { /// true means during incremental loading @Published public var isIncremental: Bool = false - var manager: SDWebImageManager? weak var currentOperation: SDWebImageOperation? = nil - var url: URL? - var options: SDWebImageOptions = [] - var context: [SDWebImageContextOption : Any]? = nil var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)? var failureBlock: ((Error) -> Void)? var progressBlock: ((Int, Int) -> Void)? - /// Create a image manager for loading the specify url, with custom options and context. + public init() {} + + /// Start to load the url operation /// - Parameter url: The image url /// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values. /// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. - public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) { - self.url = url - self.options = options - self.context = context - if let manager = context?[.customManager] as? SDWebImageManager { - self.manager = manager + public func load(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) { + let manager: SDWebImageManager + if let customManager = context?[.customManager] as? SDWebImageManager { + manager = customManager } else { - self.manager = .shared - } - } - - init() {} - - /// Start to load the url operation - public func load() { - guard let manager = manager else { - return + manager = .shared } if currentOperation != nil { return diff --git a/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift b/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift new file mode 100644 index 00000000..6a3b9873 --- /dev/null +++ b/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift @@ -0,0 +1,84 @@ +/* + * This file is part of the SDWebImage package. + * (c) DreamPiggy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import Foundation +import SwiftUI + +#if os(iOS) || os(tvOS) || os(macOS) + +@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +struct PlatformAppear: PlatformViewRepresentable { + let appearAction: () -> Void + let disappearAction: () -> Void + + #if os(iOS) || os(tvOS) + func makeUIView(context: Context) -> some UIView { + let view = PlatformAppearView() + view.appearAction = appearAction + view.disappearAction = disappearAction + return view + } + + func updateUIView(_ uiView: UIViewType, context: Context) {} + #endif + #if os(macOS) + func makeNSView(context: Context) -> some NSView { + let view = PlatformAppearView() + view.appearAction = appearAction + view.disappearAction = disappearAction + return view + } + + func updateNSView(_ nsView: NSViewType, context: Context) {} + #endif +} + +@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +class PlatformAppearView: PlatformView { + var appearAction: () -> Void = {} + var disappearAction: () -> Void = {} + + #if os(iOS) || os(tvOS) + override func willMove(toWindow newWindow: UIWindow?) { + if newWindow != nil { + appearAction() + } else { + disappearAction() + } + } + #endif + + #if os(macOS) + override func viewWillMove(toWindow newWindow: NSWindow?) { + if newWindow != nil { + appearAction() + } else { + disappearAction() + } + } + #endif +} + +#endif + +@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +extension View { + /// Used UIKit/AppKit behavior to detect the SwiftUI view's visibility. + /// This hack is because of SwiftUI 1.0/2.0 buggy behavior. The built-in `onAppear` and `onDisappear` is so massive on some cases. Where UIKit/AppKit is solid. + /// - Parameters: + /// - appear: The action when view appears + /// - disappear: The action when view disappears + /// - Returns: Some view + func onPlatformAppear(appear: @escaping () -> Void = {}, disappear: @escaping () -> Void = {}) -> some View { + #if os(iOS) || os(tvOS) || os(macOS) + return self.background(PlatformAppear(appearAction: appear, disappearAction: disappear)) + #else + return self.onAppear(perform: appear).onDisappear(perform: disappear) + #endif + } +} diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 07908861..01704998 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -9,6 +9,15 @@ import SwiftUI import SDWebImage +/// Data Binding Object, only properties in this object can support changes from user with @State and refresh +@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +final class WebImageModel : ObservableObject { + /// URL image + @Published var url: URL? + @Published var webOptions: SDWebImageOptions = [] + @Published var webContext: [SDWebImageContextOption : Any]? = nil +} + /// Completion Handler Binding Object, supports dynamic @State changes @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) final class WebImageHandler: ObservableObject { @@ -43,6 +52,9 @@ public struct WebImage : View { /// True to start animation, false to stop animation. @Binding public var isAnimating: Bool + /// A observed object to pass through the image model to manager + @ObservedObject var imageModel: WebImageModel + /// A observed object to pass through the image handler to manager @ObservedObject var imageHandler = WebImageHandler() @@ -52,25 +64,10 @@ public struct WebImage : View { /// A observed object to pass through the image manager loading status to indicator @ObservedObject var indicatorStatus = IndicatorStatus() - @SwiftUI.StateObject var imagePlayer_SwiftUI = ImagePlayer() - @Backport.StateObject var imagePlayer_Backport = ImagePlayer() - var imagePlayer: ImagePlayer { - if #available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) { - return imagePlayer_SwiftUI - } else { - return imagePlayer_Backport - } - } + @ObservedObject var imagePlayer = ImagePlayer() - @SwiftUI.StateObject var imageManager_SwiftUI = ImageManager() - @Backport.StateObject var imageManager_Backport = ImageManager() - var imageManager: ImageManager { - if #available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) { - return imageManager_SwiftUI - } else { - return imageManager_Backport - } - } + // FIXME: Use SwiftUI StateObject and remove onPlatformAppear once drop iOS 13 support + @Backport.StateObject var imageManager = ImageManager() /// Create a web image with url, placeholder, custom options and context. Optional can support animated image using Binding. /// - Parameter url: The image url @@ -86,11 +83,11 @@ public struct WebImage : View { context[.animatedImageClass] = SDAnimatedImage.self } } - if #available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) { - _imageManager_SwiftUI = SwiftUI.StateObject(wrappedValue: ImageManager(url: url, options: options, context: context)) - } else { - _imageManager_Backport = Backport.StateObject(wrappedValue: ImageManager(url: url, options: options, context: context)) - } + let imageModel = WebImageModel() + imageModel.url = url + imageModel.webOptions = options + imageModel.webContext = context + _imageModel = ObservedObject(wrappedValue: imageModel) } /// Create a web image with url, placeholder, custom options and context. @@ -128,24 +125,24 @@ public struct WebImage : View { } } else { setupPlaceholder() - .onAppear { + .onPlatformAppear(appear: { self.imageManager.successBlock = self.imageHandler.successBlock self.imageManager.failureBlock = self.imageHandler.failureBlock self.imageManager.progressBlock = self.imageHandler.progressBlock // Load remote image when first appear - self.imageManager.load() + self.imageManager.load(url: imageModel.url, options: imageModel.webOptions, context: imageModel.webContext) guard self.imageConfiguration.retryOnAppear else { return } // When using prorgessive loading, the new partial image will cause onAppear. Filter this case if self.imageManager.image == nil && !self.imageManager.isIncremental { - self.imageManager.load() + self.imageManager.load(url: imageModel.url, options: imageModel.webOptions, context: imageModel.webContext) } - }.onDisappear { + }, disappear: { guard self.imageConfiguration.cancelOnDisappear else { return } // When using prorgessive loading, the previous partial image will cause onDisappear. Filter this case if self.imageManager.image == nil && !self.imageManager.isIncremental { self.imageManager.cancel() } - }.onReceive(imageManager.objectWillChange) { _ in + }).onReceive(imageManager.objectWillChange) { _ in indicatorStatus.isLoading = imageManager.isLoading indicatorStatus.progress = imageManager.progress } @@ -228,7 +225,7 @@ public struct WebImage : View { // Don't use `Group` because it will trigger `.onAppear` and `.onDisappear` when condition view removed, treat placeholder as an entire component if let placeholder = placeholder { // If use `.delayPlaceholder`, the placeholder is applied after loading failed, hide during loading :) - if imageManager.options.contains(.delayPlaceholder) && imageManager.isLoading { + if imageModel.webOptions.contains(.delayPlaceholder) && imageManager.isLoading { return AnyView(configure(image: .empty)) } else { return placeholder diff --git a/Tests/ImageManagerTests.swift b/Tests/ImageManagerTests.swift index e5d4a099..002ab91a 100644 --- a/Tests/ImageManagerTests.swift +++ b/Tests/ImageManagerTests.swift @@ -18,7 +18,7 @@ class ImageManagerTests: XCTestCase { func testImageManager() throws { let expectation = self.expectation(description: "ImageManager usage with Combine") let imageUrl = URL(string: "https://via.placeholder.com/500x500.jpg") - let imageManager = ImageManager(url: imageUrl) + let imageManager = ImageManager() imageManager.setOnSuccess { image, cacheType, data in XCTAssertNotNil(image) expectation.fulfill() @@ -29,7 +29,7 @@ class ImageManagerTests: XCTestCase { imageManager.setOnProgress { receivedSize, expectedSize in } - imageManager.load() + imageManager.load(url: imageUrl) XCTAssertNotNil(imageManager.currentOperation) let sub = imageManager.objectWillChange .subscribe(on: RunLoop.main) From 6590afdd3a6dd4e1d6b67d45049b4c4020aafe94 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 21 Sep 2022 20:35:44 +0800 Subject: [PATCH 02/90] Use manager published IndicatorStatus to pass update the indicator Fix warning --- SDWebImageSwiftUI/Classes/ImageManager.swift | 16 +++++++--------- .../Classes/SwiftUICompatibility.swift | 18 +++++++++++++----- SDWebImageSwiftUI/Classes/WebImage.swift | 12 +++--------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index 94ce96e4..283de3c6 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -21,12 +21,10 @@ public final class ImageManager : ObservableObject { @Published public var cacheType: SDImageCacheType = .none /// loading error, you can grab the error code and reason listed in `SDWebImageErrorDomain`, to provide a user interface about the error reason @Published public var error: Error? - /// whether network is loading or cache is querying, should only be used for indicator binding - @Published public var isLoading: Bool = false - /// network progress, should only be used for indicator binding - @Published public var progress: Double = 0 /// true means during incremental loading @Published public var isIncremental: Bool = false + /// A observed object to pass through the image manager loading status to indicator + @Published public var indicatorStatus = IndicatorStatus() weak var currentOperation: SDWebImageOperation? = nil @@ -50,7 +48,7 @@ public final class ImageManager : ObservableObject { if currentOperation != nil { return } - self.isLoading = true + self.indicatorStatus.isLoading = true currentOperation = manager.loadImage(with: url, options: options, context: context, progress: { [weak self] (receivedSize, expectedSize, _) in guard let self = self else { return @@ -62,7 +60,7 @@ public final class ImageManager : ObservableObject { progress = 0 } DispatchQueue.main.async { - self.progress = progress + self.indicatorStatus.progress = progress } self.progressBlock?(receivedSize, expectedSize) }) { [weak self] (image, data, error, cacheType, finished, _) in @@ -82,8 +80,8 @@ public final class ImageManager : ObservableObject { if finished { self.imageData = data self.cacheType = cacheType - self.isLoading = false - self.progress = 1 + self.indicatorStatus.isLoading = false + self.indicatorStatus.progress = 1 if let image = image { self.successBlock?(image, data, cacheType) } else { @@ -98,8 +96,8 @@ public final class ImageManager : ObservableObject { if let operation = currentOperation { operation.cancel() currentOperation = nil - isLoading = false } + indicatorStatus.isLoading = false } } diff --git a/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift b/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift index 6a3b9873..f2cb6dd2 100644 --- a/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift +++ b/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift @@ -46,9 +46,13 @@ class PlatformAppearView: PlatformView { #if os(iOS) || os(tvOS) override func willMove(toWindow newWindow: UIWindow?) { if newWindow != nil { - appearAction() + DispatchQueue.main.async { + self.appearAction() + } } else { - disappearAction() + DispatchQueue.main.async { + self.disappearAction() + } } } #endif @@ -56,9 +60,13 @@ class PlatformAppearView: PlatformView { #if os(macOS) override func viewWillMove(toWindow newWindow: NSWindow?) { if newWindow != nil { - appearAction() + DispatchQueue.main.async { + self.appearAction() + } } else { - disappearAction() + DispatchQueue.main.async { + self.disappearAction() + } } } #endif @@ -76,7 +84,7 @@ extension View { /// - Returns: Some view func onPlatformAppear(appear: @escaping () -> Void = {}, disappear: @escaping () -> Void = {}) -> some View { #if os(iOS) || os(tvOS) || os(macOS) - return self.background(PlatformAppear(appearAction: appear, disappearAction: disappear)) + return self.overlay(PlatformAppear(appearAction: appear, disappearAction: disappear)) #else return self.onAppear(perform: appear).onDisappear(perform: disappear) #endif diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 01704998..dff6c739 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -61,9 +61,6 @@ public struct WebImage : View { /// A observed object to pass through the image configuration to player @ObservedObject var imageConfiguration = WebImageConfiguration() - /// A observed object to pass through the image manager loading status to indicator - @ObservedObject var indicatorStatus = IndicatorStatus() - @ObservedObject var imagePlayer = ImagePlayer() // FIXME: Use SwiftUI StateObject and remove onPlatformAppear once drop iOS 13 support @@ -142,10 +139,7 @@ public struct WebImage : View { if self.imageManager.image == nil && !self.imageManager.isIncremental { self.imageManager.cancel() } - }).onReceive(imageManager.objectWillChange) { _ in - indicatorStatus.isLoading = imageManager.isLoading - indicatorStatus.progress = imageManager.progress - } + }) } } } @@ -225,7 +219,7 @@ public struct WebImage : View { // Don't use `Group` because it will trigger `.onAppear` and `.onDisappear` when condition view removed, treat placeholder as an entire component if let placeholder = placeholder { // If use `.delayPlaceholder`, the placeholder is applied after loading failed, hide during loading :) - if imageModel.webOptions.contains(.delayPlaceholder) && imageManager.isLoading { + if imageModel.webOptions.contains(.delayPlaceholder) && imageManager.indicatorStatus.isLoading { return AnyView(configure(image: .empty)) } else { return placeholder @@ -352,7 +346,7 @@ extension WebImage { /// Associate a indicator when loading image with url /// - Parameter indicator: The indicator type, see `Indicator` public func indicator(_ indicator: Indicator) -> some View where T : View { - return self.modifier(IndicatorViewModifier(status: indicatorStatus, indicator: indicator)) + return self.modifier(IndicatorViewModifier(status: imageManager.indicatorStatus, indicator: indicator)) } /// Associate a indicator when loading image with url, convenient method with block From ce5340fd08ca48ec0f99d03a989c5ad60d83e338 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 21 Sep 2022 20:45:12 +0800 Subject: [PATCH 03/90] Fix the delayPlaceholder behavior and Player behavior when parent View change state --- SDWebImageSwiftUI/Classes/WebImage.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index dff6c739..6ed6a660 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -61,7 +61,8 @@ public struct WebImage : View { /// A observed object to pass through the image configuration to player @ObservedObject var imageConfiguration = WebImageConfiguration() - @ObservedObject var imagePlayer = ImagePlayer() + // FIXME: Use SwiftUI StateObject and remove onPlatformAppear once drop iOS 13 support + @Backport.StateObject var imagePlayer = ImagePlayer() // FIXME: Use SwiftUI StateObject and remove onPlatformAppear once drop iOS 13 support @Backport.StateObject var imageManager = ImageManager() @@ -219,7 +220,7 @@ public struct WebImage : View { // Don't use `Group` because it will trigger `.onAppear` and `.onDisappear` when condition view removed, treat placeholder as an entire component if let placeholder = placeholder { // If use `.delayPlaceholder`, the placeholder is applied after loading failed, hide during loading :) - if imageModel.webOptions.contains(.delayPlaceholder) && imageManager.indicatorStatus.isLoading { + if imageModel.webOptions.contains(.delayPlaceholder) && imageManager.error == nil { return AnyView(configure(image: .empty)) } else { return placeholder From d281bde03772687fa0bf659ac1c7788ec186c270 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 21 Sep 2022 23:01:07 +0800 Subject: [PATCH 04/90] Fix the State change behavior again Using the `StateObject` to check and refresh to the latest status, using `currentURL` and `currentAnimatedImage` to check lazily --- .../SDWebImageSwiftUIDemo/ContentView.swift | 36 +++++++++++++++ SDWebImageSwiftUI.xcodeproj/project.pbxproj | 10 ++++ SDWebImageSwiftUI/Classes/ImageManager.swift | 9 ++-- SDWebImageSwiftUI/Classes/ImagePlayer.swift | 5 +- .../Classes/SwiftUICompatibility.swift | 2 +- SDWebImageSwiftUI/Classes/WebImage.swift | 46 ++++++++++++------- 6 files changed, 87 insertions(+), 21 deletions(-) diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index 4c6ea01d..fae1c09c 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -34,6 +34,42 @@ extension Indicator where T == ProgressView { } #endif +// Test Switching url using @State +struct ContentView2: View { + @State var imageURLs = [ + "https://raw.githubusercontent.com/recurser/exif-orientation-examples/master/Landscape_1.jpg", + "https://raw.githubusercontent.com/recurser/exif-orientation-examples/master/Landscape_2.jpg", + "http://assets.sbnation.com/assets/2512203/dogflops.gif", + "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif" + ] + @State var animated: Bool = false // You can change between WebImage/AnimatedImage + @State var imageIndex : Int = 0 + var body: some View { + Group { + Text("\(animated ? "AnimatedImage" : "WebImage") - \((imageURLs[imageIndex] as NSString).lastPathComponent)") + Spacer() + if self.animated { + AnimatedImage(url:URL(string: imageURLs[imageIndex])) + .resizable() + .aspectRatio(contentMode: .fit) + } else { + WebImage(url:URL(string: imageURLs[imageIndex])) + .resizable() + .aspectRatio(contentMode: .fit) + } + Spacer() + Button("Next") { + if imageIndex + 1 >= imageURLs.count { + imageIndex = 0 + } else { + imageIndex += 1 + } + } + Toggle("Switch", isOn: $animated) + } + } +} + struct ContentView: View { @State var imageURLs = [ "http://assets.sbnation.com/assets/2512203/dogflops.gif", diff --git a/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/SDWebImageSwiftUI.xcodeproj/project.pbxproj index 9bacddc1..1fbde810 100644 --- a/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -23,6 +23,10 @@ 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 */; }; + 32B79C9528DB40430088C432 /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */; }; + 32B79C9628DB40430088C432 /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */; }; + 32B79C9728DB40430088C432 /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */; }; + 32B79C9828DB40430088C432 /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */; }; 32B933E523659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; 32B933E623659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; 32B933E723659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; @@ -87,6 +91,7 @@ 326B8486236335110011BDFB /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; 326B848B236335400011BDFB /* ProgressIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressIndicator.swift; sourceTree = ""; }; 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewWrapper.swift; sourceTree = ""; }; + 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUICompatibility.swift; sourceTree = ""; }; 32B933E423659A1900BB7CAD /* Transition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transition.swift; sourceTree = ""; }; 32BC086F28D23D35002451BD /* StateObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateObject.swift; sourceTree = ""; }; 32BC087028D23D35002451BD /* OnChange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnChange.swift; sourceTree = ""; }; @@ -233,6 +238,7 @@ 32C43DDE22FD54C600BE87F5 /* WebImage.swift */, 32C43DDF22FD54C600BE87F5 /* AnimatedImage.swift */, 32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */, + 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */, 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */, 32D26A012446B546005905DA /* Image.swift */, ); @@ -457,6 +463,7 @@ 326B84822363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3222FD5DE100BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480A23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, + 32B79C9528DB40430088C432 /* SwiftUICompatibility.swift in Sources */, 326B8487236335110011BDFB /* ActivityIndicator.swift in Sources */, 32C43E1622FD583700BE87F5 /* ImageManager.swift in Sources */, 32C43E1822FD583700BE87F5 /* AnimatedImage.swift in Sources */, @@ -479,6 +486,7 @@ 326B84832363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3322FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, + 32B79C9628DB40430088C432 /* SwiftUICompatibility.swift in Sources */, 326B8488236335110011BDFB /* ActivityIndicator.swift in Sources */, 32C43E1922FD583700BE87F5 /* ImageManager.swift in Sources */, 32C43E1B22FD583700BE87F5 /* AnimatedImage.swift in Sources */, @@ -501,6 +509,7 @@ 326B84842363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3422FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, + 32B79C9728DB40430088C432 /* SwiftUICompatibility.swift in Sources */, 326B8489236335110011BDFB /* ActivityIndicator.swift in Sources */, 32C43E1C22FD583800BE87F5 /* ImageManager.swift in Sources */, 32C43E1E22FD583800BE87F5 /* AnimatedImage.swift in Sources */, @@ -523,6 +532,7 @@ 326B84852363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3522FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, + 32B79C9828DB40430088C432 /* SwiftUICompatibility.swift in Sources */, 326B848A236335110011BDFB /* ActivityIndicator.swift in Sources */, 32C43E1F22FD583800BE87F5 /* ImageManager.swift in Sources */, 32C43E2122FD583800BE87F5 /* AnimatedImage.swift in Sources */, diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index 283de3c6..b8d5fe49 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -27,7 +27,8 @@ public final class ImageManager : ObservableObject { @Published public var indicatorStatus = IndicatorStatus() weak var currentOperation: SDWebImageOperation? = nil - + + var currentURL: URL? var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)? var failureBlock: ((Error) -> Void)? var progressBlock: ((Int, Int) -> Void)? @@ -45,10 +46,12 @@ public final class ImageManager : ObservableObject { } else { manager = .shared } - if currentOperation != nil { + if (currentOperation != nil && currentURL == url) { return } - self.indicatorStatus.isLoading = true + currentURL = url + indicatorStatus.isLoading = true + indicatorStatus.progress = 0 currentOperation = manager.loadImage(with: url, options: options, context: context, progress: { [weak self] (receivedSize, expectedSize, _) in guard let self = self else { return diff --git a/SDWebImageSwiftUI/Classes/ImagePlayer.swift b/SDWebImageSwiftUI/Classes/ImagePlayer.swift index 4f25f363..e0ceded6 100644 --- a/SDWebImageSwiftUI/Classes/ImagePlayer.swift +++ b/SDWebImageSwiftUI/Classes/ImagePlayer.swift @@ -45,6 +45,8 @@ public final class ImagePlayer : ObservableObject { /// Current playing loop count @Published public var currentLoopCount: UInt = 0 + var currentAnimatedImage: (PlatformImage & SDAnimatedImageProvider)? + /// Whether current player is valid for playing. This will check the internal player exist or not public var isValid: Bool { player != nil @@ -97,10 +99,11 @@ public final class ImagePlayer : ObservableObject { /// Setup the player using Animated Image. /// After setup, you can always check `isValid` status, or call `startPlaying` to play the animation. /// - Parameter image: animated image - public func setupPlayer(animatedImage: SDAnimatedImageProvider) { + public func setupPlayer(animatedImage: PlatformImage & SDAnimatedImageProvider) { if isValid { return } + currentAnimatedImage = animatedImage if let imagePlayer = SDAnimatedImagePlayer(provider: animatedImage) { imagePlayer.animationFrameHandler = { [weak self] (index, frame) in self?.currentFrameIndex = index diff --git a/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift b/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift index f2cb6dd2..e7a46393 100644 --- a/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift +++ b/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift @@ -84,7 +84,7 @@ extension View { /// - Returns: Some view func onPlatformAppear(appear: @escaping () -> Void = {}, disappear: @escaping () -> Void = {}) -> some View { #if os(iOS) || os(tvOS) || os(macOS) - return self.overlay(PlatformAppear(appearAction: appear, disappearAction: disappear)) + return self.background(PlatformAppear(appearAction: appear, disappearAction: disappear)) #else return self.onAppear(perform: appear).onDisappear(perform: disappear) #endif diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 6ed6a660..97b896d1 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -14,8 +14,8 @@ import SDWebImage final class WebImageModel : ObservableObject { /// URL image @Published var url: URL? - @Published var webOptions: SDWebImageOptions = [] - @Published var webContext: [SDWebImageContextOption : Any]? = nil + @Published var options: SDWebImageOptions = [] + @Published var context: [SDWebImageContextOption : Any]? = nil } /// Completion Handler Binding Object, supports dynamic @State changes @@ -61,11 +61,13 @@ public struct WebImage : View { /// A observed object to pass through the image configuration to player @ObservedObject var imageConfiguration = WebImageConfiguration() + @ObservedObject var indicatorStatus : IndicatorStatus + // FIXME: Use SwiftUI StateObject and remove onPlatformAppear once drop iOS 13 support @Backport.StateObject var imagePlayer = ImagePlayer() // FIXME: Use SwiftUI StateObject and remove onPlatformAppear once drop iOS 13 support - @Backport.StateObject var imageManager = ImageManager() + @Backport.StateObject var imageManager : ImageManager /// Create a web image with url, placeholder, custom options and context. Optional can support animated image using Binding. /// - Parameter url: The image url @@ -83,9 +85,12 @@ public struct WebImage : View { } let imageModel = WebImageModel() imageModel.url = url - imageModel.webOptions = options - imageModel.webContext = context + imageModel.options = options + imageModel.context = context _imageModel = ObservedObject(wrappedValue: imageModel) + let imageManager = ImageManager() + _imageManager = Backport.StateObject(wrappedValue: imageManager) + _indicatorStatus = ObservedObject(wrappedValue: imageManager.indicatorStatus) } /// Create a web image with url, placeholder, custom options and context. @@ -98,7 +103,7 @@ public struct WebImage : View { public var body: some View { return Group { - if let image = imageManager.image { + if imageManager.image != nil && imageModel.url == imageManager.currentURL { if isAnimating && !imageManager.isIncremental { setupPlayer() .onDisappear { @@ -118,7 +123,7 @@ public struct WebImage : View { if let currentFrame = imagePlayer.currentFrame { configure(image: currentFrame) } else { - configure(image: image) + configure(image: imageManager.image!) } } } else { @@ -127,17 +132,19 @@ public struct WebImage : View { self.imageManager.successBlock = self.imageHandler.successBlock self.imageManager.failureBlock = self.imageHandler.failureBlock self.imageManager.progressBlock = self.imageHandler.progressBlock - // Load remote image when first appear - self.imageManager.load(url: imageModel.url, options: imageModel.webOptions, context: imageModel.webContext) + if (self.imageManager.error == nil) { + // Load remote image when first appear + self.imageManager.load(url: imageModel.url, options: imageModel.options, context: imageModel.context) + } guard self.imageConfiguration.retryOnAppear else { return } // When using prorgessive loading, the new partial image will cause onAppear. Filter this case - if self.imageManager.image == nil && !self.imageManager.isIncremental { - self.imageManager.load(url: imageModel.url, options: imageModel.webOptions, context: imageModel.webContext) + if self.imageManager.error != nil && !self.imageManager.isIncremental { + self.imageManager.load(url: imageModel.url, options: imageModel.options, context: imageModel.context) } }, disappear: { guard self.imageConfiguration.cancelOnDisappear else { return } // When using prorgessive loading, the previous partial image will cause onDisappear. Filter this case - if self.imageManager.image == nil && !self.imageManager.isIncremental { + if self.imageManager.error != nil && !self.imageManager.isIncremental { self.imageManager.cancel() } }) @@ -196,18 +203,25 @@ public struct WebImage : View { /// Animated Image Support func setupPlayer() -> some View { - if let currentFrame = imagePlayer.currentFrame { + if let currentFrame = imagePlayer.currentFrame, imagePlayer.currentAnimatedImage == imageManager.image! { return configure(image: currentFrame).onAppear { self.imagePlayer.startPlaying() } } else { return configure(image: imageManager.image!).onAppear { - if let animatedImage = imageManager.image as? SDAnimatedImageProvider { + self.imagePlayer.stopPlaying() + if let animatedImage = imageManager.image as? PlatformImage & SDAnimatedImageProvider { + // Clear previous status + self.imagePlayer.player = nil; + self.imagePlayer.currentFrame = nil; + self.imagePlayer.currentFrameIndex = 0; + self.imagePlayer.currentLoopCount = 0; self.imagePlayer.customLoopCount = self.imageConfiguration.customLoopCount self.imagePlayer.maxBufferSize = self.imageConfiguration.maxBufferSize self.imagePlayer.runLoopMode = self.imageConfiguration.runLoopMode self.imagePlayer.playbackMode = self.imageConfiguration.playbackMode self.imagePlayer.playbackRate = self.imageConfiguration.playbackRate + // Setup new player self.imagePlayer.setupPlayer(animatedImage: animatedImage) self.imagePlayer.startPlaying() } @@ -220,7 +234,7 @@ public struct WebImage : View { // Don't use `Group` because it will trigger `.onAppear` and `.onDisappear` when condition view removed, treat placeholder as an entire component if let placeholder = placeholder { // If use `.delayPlaceholder`, the placeholder is applied after loading failed, hide during loading :) - if imageModel.webOptions.contains(.delayPlaceholder) && imageManager.error == nil { + if imageModel.options.contains(.delayPlaceholder) && imageManager.error == nil { return AnyView(configure(image: .empty)) } else { return placeholder @@ -347,7 +361,7 @@ extension WebImage { /// Associate a indicator when loading image with url /// - Parameter indicator: The indicator type, see `Indicator` public func indicator(_ indicator: Indicator) -> some View where T : View { - return self.modifier(IndicatorViewModifier(status: imageManager.indicatorStatus, indicator: indicator)) + return self.modifier(IndicatorViewModifier(status: indicatorStatus, indicator: indicator)) } /// Associate a indicator when loading image with url, convenient method with block From 1d7efeccc4b9bc0915450ea29aeca69fec97326c Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 22 Sep 2022 00:00:40 +0800 Subject: [PATCH 05/90] Fix the empty placeholder may cause onAppear does not called Move the reset logic into helper function setupManager --- .../SDWebImageSwiftUIDemo/ContentView.swift | 4 ++ SDWebImageSwiftUI/Classes/ImageManager.swift | 1 + SDWebImageSwiftUI/Classes/WebImage.swift | 65 ++++++++++++------- 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index fae1c09c..18b73ce3 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -65,6 +65,10 @@ struct ContentView2: View { imageIndex += 1 } } + Button("Reload") { + SDImageCache.shared.clearMemory() + SDImageCache.shared.clearDisk(onCompletion: nil) + } Toggle("Switch", isOn: $animated) } } diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index b8d5fe49..d9bce8fa 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -101,6 +101,7 @@ public final class ImageManager : ObservableObject { currentOperation = nil } indicatorStatus.isLoading = false + currentURL = nil } } diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 97b896d1..350e2dd7 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -103,22 +103,10 @@ public struct WebImage : View { public var body: some View { return Group { + // Render Logic if imageManager.image != nil && imageModel.url == imageManager.currentURL { if isAnimating && !imageManager.isIncremental { setupPlayer() - .onDisappear { - // Only stop the player which is not intermediate status - if !imagePlayer.isWaiting { - if self.imageConfiguration.pausable { - self.imagePlayer.pausePlaying() - } else { - self.imagePlayer.stopPlaying() - } - if self.imageConfiguration.purgeable { - self.imagePlayer.clearFrameBuffer() - } - } - } } else { if let currentFrame = imagePlayer.currentFrame { configure(image: currentFrame) @@ -127,11 +115,10 @@ public struct WebImage : View { } } } else { + // Load Logic setupPlaceholder() .onPlatformAppear(appear: { - self.imageManager.successBlock = self.imageHandler.successBlock - self.imageManager.failureBlock = self.imageHandler.failureBlock - self.imageManager.progressBlock = self.imageHandler.progressBlock + setupManager() if (self.imageManager.error == nil) { // Load remote image when first appear self.imageManager.load(url: imageModel.url, options: imageModel.options, context: imageModel.context) @@ -201,14 +188,46 @@ public struct WebImage : View { } } + /// Image Manager status + func setupManager() { + self.imageManager.successBlock = self.imageHandler.successBlock + self.imageManager.failureBlock = self.imageHandler.failureBlock + self.imageManager.progressBlock = self.imageHandler.progressBlock + if imageModel.url != imageManager.currentURL { + imageManager.cancel() + imageManager.image = nil + imageManager.imageData = nil + imageManager.cacheType = .none + imageManager.error = nil + imageManager.isIncremental = false + imageManager.indicatorStatus.isLoading = false + imageManager.indicatorStatus.progress = 0 + } + } + /// Animated Image Support func setupPlayer() -> some View { + let disappearAction = { + // Only stop the player which is not intermediate status + if !imagePlayer.isWaiting { + if self.imageConfiguration.pausable { + self.imagePlayer.pausePlaying() + } else { + self.imagePlayer.stopPlaying() + } + if self.imageConfiguration.purgeable { + self.imagePlayer.clearFrameBuffer() + } + } + } if let currentFrame = imagePlayer.currentFrame, imagePlayer.currentAnimatedImage == imageManager.image! { - return configure(image: currentFrame).onAppear { + return configure(image: currentFrame).onPlatformAppear(appear: { self.imagePlayer.startPlaying() - } + }, disappear: { + disappearAction() + }) } else { - return configure(image: imageManager.image!).onAppear { + return configure(image: imageManager.image!).onPlatformAppear(appear: { self.imagePlayer.stopPlaying() if let animatedImage = imageManager.image as? PlatformImage & SDAnimatedImageProvider { // Clear previous status @@ -225,7 +244,9 @@ public struct WebImage : View { self.imagePlayer.setupPlayer(animatedImage: animatedImage) self.imagePlayer.startPlaying() } - } + }, disappear: { + disappearAction() + }) } } @@ -235,12 +256,12 @@ public struct WebImage : View { if let placeholder = placeholder { // If use `.delayPlaceholder`, the placeholder is applied after loading failed, hide during loading :) if imageModel.options.contains(.delayPlaceholder) && imageManager.error == nil { - return AnyView(configure(image: .empty)) + return AnyView(configure(image: .empty).id(UUID())) // UUID to avoid SwiftUI engine cache the status and does not call `onAppear` } else { return placeholder } } else { - return AnyView(configure(image: .empty)) + return AnyView(configure(image: .empty).id(UUID())) // UUID to avoid SwiftUI engine cache the status and does not call `onAppear` } } } From d18693909b6f17fe0024478cc582b6d7ac9a8a89 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 22 Sep 2022 00:13:22 +0800 Subject: [PATCH 06/90] Fix watchOS demo compile issue --- Example/SDWebImageSwiftUIDemo/ContentView.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index 18b73ce3..fc17b74d 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -48,6 +48,11 @@ struct ContentView2: View { Group { Text("\(animated ? "AnimatedImage" : "WebImage") - \((imageURLs[imageIndex] as NSString).lastPathComponent)") Spacer() + #if os(watchOS) + WebImage(url:URL(string: imageURLs[imageIndex])) + .resizable() + .aspectRatio(contentMode: .fit) + #else if self.animated { AnimatedImage(url:URL(string: imageURLs[imageIndex])) .resizable() @@ -57,6 +62,7 @@ struct ContentView2: View { .resizable() .aspectRatio(contentMode: .fit) } + #endif Spacer() Button("Next") { if imageIndex + 1 >= imageURLs.count { From abd9102f6b4db9d6b07ed6e2fbc7889120ae6078 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 22 Sep 2022 15:03:20 +0800 Subject: [PATCH 07/90] Update the readme about when using in List/LazyStack/LazyGrid --- .../SDWebImageSwiftUIDemo/ContentView.swift | 97 +++++++++++-------- README.md | 58 +++++++++-- SDWebImageSwiftUI/Classes/WebImage.swift | 10 +- 3 files changed, 111 insertions(+), 54 deletions(-) diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index fc17b74d..a3619c90 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -104,6 +104,58 @@ struct ContentView: View { @State var animated: Bool = false // You can change between WebImage/AnimatedImage @EnvironmentObject var settings: UserSettings + // Used to avoid https://twitter.com/fatbobman/status/1572507700436807683?s=20&t=5rfj6BUza5Jii-ynQatCFA + struct ItemView: View { + @Binding var animated: Bool + @State var url: String + var body: some View { + NavigationLink(destination: DetailView(url: url, animated: self.animated)) { + HStack { + if self.animated { + #if os(macOS) || os(iOS) || os(tvOS) + AnimatedImage(url: URL(string:url), isAnimating: .constant(true)) + .onViewUpdate { view, context in + #if os(macOS) + view.toolTip = url + #endif + } + .indicator(SDWebImageActivityIndicator.medium) + /** + .placeholder(UIImage(systemName: "photo")) + */ + .transition(.fade) + .resizable() + .scaledToFit() + .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) + #else + WebImage(url: URL(string:url), isAnimating: self.$animated) + .resizable() + .indicator(.activity) + .transition(.fade(duration: 0.5)) + .scaledToFit() + .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) + #endif + } else { + WebImage(url: URL(string:url), isAnimating: .constant(true)) + .resizable() + /** + .placeholder { + Image(systemName: "photo") + } + */ + .indicator(.activity) + .transition(.fade(duration: 0.5)) + .scaledToFit() + .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) + } + Text((url as NSString).lastPathComponent) + } + } + .buttonStyle(PlainButtonStyle()) + } + } + + var body: some View { #if os(iOS) return NavigationView { @@ -165,49 +217,8 @@ struct ContentView: View { func contentView() -> some View { List { ForEach(imageURLs, id: \.self) { url in - NavigationLink(destination: DetailView(url: url, animated: self.animated)) { - HStack { - if self.animated { - #if os(macOS) || os(iOS) || os(tvOS) - AnimatedImage(url: URL(string:url), isAnimating: .constant(true)) - .onViewUpdate { view, context in - #if os(macOS) - view.toolTip = url - #endif - } - .indicator(SDWebImageActivityIndicator.medium) - /** - .placeholder(UIImage(systemName: "photo")) - */ - .transition(.fade) - .resizable() - .scaledToFit() - .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) - #else - WebImage(url: URL(string:url), isAnimating: self.$animated) - .resizable() - .indicator(.activity) - .transition(.fade(duration: 0.5)) - .scaledToFit() - .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) - #endif - } else { - WebImage(url: URL(string:url), isAnimating: .constant(true)) - .resizable() - /** - .placeholder { - Image(systemName: "photo") - } - */ - .indicator(.activity) - .transition(.fade(duration: 0.5)) - .scaledToFit() - .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) - } - Text((url as NSString).lastPathComponent) - } - } - .buttonStyle(PlainButtonStyle()) + // Must use top level view instead of inlined view structure + ItemView(animated: $animated, url: url) } .onDelete { indexSet in indexSet.forEach { index in diff --git a/README.md b/README.md index b9007e6e..01c39e8e 100644 --- a/README.md +++ b/README.md @@ -270,7 +270,7 @@ It looks familiar like `SDWebImageManager`, but it's built for SwiftUI world, wh ```swift struct MyView : View { - @ObservedObject var imageManager: ImageManager + @ObservedObject var imageManager = ImageManager() var body: some View { // Your custom complicated view graph Group { @@ -281,17 +281,11 @@ struct MyView : View { } } // Trigger image loading when appear - .onAppear { self.imageManager.load() } + .onAppear { self.imageManager.load(url: url) } // Cancel image loading when disappear .onDisappear { self.imageManager.cancel() } } } - -struct MyView_Previews: PreviewProvider { - static var previews: some View { - MyView(imageManager: ImageManager(url: URL(string: "https://via.placeholder.com/200x200.jpg")) - } -} ``` ### Customization and configuration setup @@ -337,6 +331,54 @@ For more information, it's really recommended to check our demo, to learn detail ### Common Problems +#### Using WebImage/AnimatedImage in List/LazyStack/LazyGrid and ForEach + +SwiftUI has a known behavior(bug?) when using stateful view in `List/LazyStack/LazyGrid`. +Only the **Top Level** view can hold its own `@State/@StateObject`, but the sub structure will lose state when scroll out of screen. +However, WebImage/Animated is both stateful. To ensure the state keep in sync even when scroll out of screen. you may use some tricks. + +See more: https://twitter.com/fatbobman/status/1572507700436807683?s=21&t=z4FkAWTMvjsgL-wKdJGreQ + +In short, it's not recommanded to do so: + +```swift +struct ContentView { + @State var imageURLs: [String] + var body: some View { + List { + ForEach(imageURLs, id: \.self) { url in + VStack { + WebImage(url) // The top level is `VStack` + } + } + } + } +} +``` + +instead, using this approach: + +```swift +struct ContentView { + struct BodyView { + @State var url: String + var body: some View { + VStack { + WebImage(url) + } + } + } + @State var imageURLs: [String] + var body: some View { + List { + ForEach(imageURLs, id: \.self) { url in + BodyView(url: url) + } + } + } +} +``` + #### Using Image/WebImage/AnimatedImage in Button/NavigationLink SwiftUI's `Button` apply overlay to its content (except `Text`) by default, this is common mistake to write code like this, which cause strange behavior: diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 350e2dd7..5e8f0bc9 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -253,16 +253,20 @@ public struct WebImage : View { /// Placeholder View Support func setupPlaceholder() -> some View { // Don't use `Group` because it will trigger `.onAppear` and `.onDisappear` when condition view removed, treat placeholder as an entire component + let result: AnyView if let placeholder = placeholder { // If use `.delayPlaceholder`, the placeholder is applied after loading failed, hide during loading :) if imageModel.options.contains(.delayPlaceholder) && imageManager.error == nil { - return AnyView(configure(image: .empty).id(UUID())) // UUID to avoid SwiftUI engine cache the status and does not call `onAppear` + result = AnyView(configure(image: .empty)) } else { - return placeholder + result = placeholder } } else { - return AnyView(configure(image: .empty).id(UUID())) // UUID to avoid SwiftUI engine cache the status and does not call `onAppear` + result = AnyView(configure(image: .empty)) } + // UUID to avoid SwiftUI engine cache the status, and does not call `onAppear` when placeholder not changed (See `ContentView.swift/ContentView2` case) + // Because we load the image url in `onAppear`, it should be called to sync with state changes :) + return result.id(UUID()) } } From 5a5690e2dcac70f3bfcc93b5729c19ae9b711d2c Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 22 Sep 2022 16:06:07 +0800 Subject: [PATCH 08/90] Fix the test case project --- .../project.pbxproj | 48 +++++++++++++++++-- Tests/AnimatedImageTests.swift | 2 +- Tests/ImageManagerTests.swift | 2 +- Tests/WebImageTests.swift | 2 +- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj index 36071150..c370771a 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -34,6 +34,8 @@ 322E0E2228D332130003A55F /* Images.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 322E0DF228D331A20003A55F /* Images.bundle */; }; 322E0E2328D332130003A55F /* Images.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 322E0DF228D331A20003A55F /* Images.bundle */; }; 326B0D712345C01900D28269 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; + 327B90F228DC4EBB003E8BD9 /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 327B90F128DC4EBB003E8BD9 /* ViewInspector */; }; + 327B90F428DC4EC0003E8BD9 /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 327B90F328DC4EC0003E8BD9 /* ViewInspector */; }; 32DCFE9528D333E8001A17BF /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 32DCFE9428D333E8001A17BF /* ViewInspector */; }; 32E5290C2348A0C700EA46FF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E5290B2348A0C700EA46FF /* AppDelegate.swift */; }; 32E529102348A0C900EA46FF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32E5290F2348A0C900EA46FF /* Assets.xcassets */; }; @@ -220,6 +222,7 @@ buildActionMask = 2147483647; files = ( 833A61715BAAB31702D867CC /* Pods_SDWebImageSwiftUITests_macOS.framework in Frameworks */, + 327B90F228DC4EBB003E8BD9 /* ViewInspector in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -228,6 +231,7 @@ buildActionMask = 2147483647; files = ( 2E3D81A12C757E01A3C420F2 /* Pods_SDWebImageSwiftUITests_tvOS.framework in Frameworks */, + 327B90F428DC4EC0003E8BD9 /* ViewInspector in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -517,9 +521,13 @@ buildRules = ( ); dependencies = ( + 327B90EE28DC4EAA003E8BD9 /* PBXTargetDependency */, 322E0E0728D331F00003A55F /* PBXTargetDependency */, ); name = "SDWebImageSwiftUITests macOS"; + packageProductDependencies = ( + 327B90F128DC4EBB003E8BD9 /* ViewInspector */, + ); productName = "SDWebImageSwiftUITests macOS"; productReference = 322E0E0228D331F00003A55F /* SDWebImageSwiftUITests macOS.xctest */; productType = "com.apple.product-type.bundle.unit-test"; @@ -537,9 +545,13 @@ buildRules = ( ); dependencies = ( + 327B90F028DC4EAE003E8BD9 /* PBXTargetDependency */, 322E0E1428D332050003A55F /* PBXTargetDependency */, ); name = "SDWebImageSwiftUITests tvOS"; + packageProductDependencies = ( + 327B90F328DC4EC0003E8BD9 /* ViewInspector */, + ); productName = "SDWebImageSwiftUITests tvOS"; productReference = 322E0E0F28D332050003A55F /* SDWebImageSwiftUITests tvOS.xctest */; productType = "com.apple.product-type.bundle.unit-test"; @@ -698,7 +710,7 @@ ); mainGroup = 607FACC71AFB9204008FA782; packageReferences = ( - 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector.git" */, + 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */, ); productRefGroup = 607FACD11AFB9204008FA782 /* Products */; projectDirPath = ""; @@ -1225,6 +1237,14 @@ target = 32E5291F2348A0D300EA46FF /* SDWebImageSwiftUIDemo-tvOS */; targetProxy = 322E0E1328D332050003A55F /* PBXContainerItemProxy */; }; + 327B90EE28DC4EAA003E8BD9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = 327B90ED28DC4EAA003E8BD9 /* ViewInspector */; + }; + 327B90F028DC4EAE003E8BD9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = 327B90EF28DC4EAE003E8BD9 /* ViewInspector */; + }; 32DCFE9728D333F1001A17BF /* PBXTargetDependency */ = { isa = PBXTargetDependency; productRef = 32DCFE9628D333F1001A17BF /* ViewInspector */; @@ -2044,7 +2064,7 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector.git" */ = { + 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/nalexn/ViewInspector.git"; requirement = { @@ -2055,14 +2075,34 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 327B90ED28DC4EAA003E8BD9 /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; + 327B90EF28DC4EAE003E8BD9 /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; + 327B90F128DC4EBB003E8BD9 /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; + 327B90F328DC4EC0003E8BD9 /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; 32DCFE9428D333E8001A17BF /* ViewInspector */ = { isa = XCSwiftPackageProductDependency; - package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector.git" */; + package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; productName = ViewInspector; }; 32DCFE9628D333F1001A17BF /* ViewInspector */ = { isa = XCSwiftPackageProductDependency; - package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector.git" */; + package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; productName = ViewInspector; }; /* End XCSwiftPackageProductDependency section */ diff --git a/Tests/AnimatedImageTests.swift b/Tests/AnimatedImageTests.swift index 60091848..29472033 100644 --- a/Tests/AnimatedImageTests.swift +++ b/Tests/AnimatedImageTests.swift @@ -182,7 +182,7 @@ class AnimatedImageTests: XCTestCase { .animation(.easeInOut) _ = try introspectView.inspect() ViewHosting.host(view: introspectView) - self.waitForExpectations(timeout: 5, handler: nil) + self.waitForExpectations(timeout: 10, handler: nil) ViewHosting.expel() } } diff --git a/Tests/ImageManagerTests.swift b/Tests/ImageManagerTests.swift index 002ab91a..ae3269b9 100644 --- a/Tests/ImageManagerTests.swift +++ b/Tests/ImageManagerTests.swift @@ -38,6 +38,6 @@ class ImageManagerTests: XCTestCase { print(value) } sub.cancel() - self.waitForExpectations(timeout: 5, handler: nil) + self.waitForExpectations(timeout: 10, handler: nil) } } diff --git a/Tests/WebImageTests.swift b/Tests/WebImageTests.swift index 28fcbefb..fce4d24c 100644 --- a/Tests/WebImageTests.swift +++ b/Tests/WebImageTests.swift @@ -23,7 +23,7 @@ class WebImageTests: XCTestCase { let imageView = WebImage(url: imageUrl) let introspectView = imageView.onSuccess { image, data, cacheType in #if os(macOS) - let displayImage = try? imageView.inspect().group().image(0).actualImage.nsImage() + let displayImage = try? imageView.inspect().group().image(0).actualImage().nsImage() #else let displayImage = try? imageView.inspect().group().image(0).actualImage().cgImage() #endif From 2221b4fde496a2df854069c432c6d53ec0bc9d59 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 22 Sep 2022 16:36:16 +0800 Subject: [PATCH 09/90] Released v2.2.0 version Update the CHANGELOG.md --- CHANGELOG.md | 11 +++++++++++ README.md | 2 +- SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1492f0b1..644c59f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.2.0] - 2022-09-22 + +### Fixed + +- Fix iOS 13 compatibility #232 +- Fix WebImage/Animated using @State to publish changes +- Al v2.1.0 users are recommend to update + +### Changed +- ImageManager API changes. The init method has no args, use `load(url:options:context:)` instead + ## [2.1.0] - 2022-09-15 ### Fixed diff --git a/README.md b/README.md index 01c39e8e..d64f3c3f 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ var body: some View { Note: However, many differences behavior between iOS 13/14's is hard to fixup. And we may break some APIs (which are not designed to be public) to fixup it. -Due to maintain issue, in the future release, we will drop the iOS 13 supports and always match SwiftUI 2.0's behavior. And **v2.1** may be the last version support iOS 13. +Due to maintain issue, in the future release, we will drop the iOS 13 supports and always match SwiftUI 2.0's behavior. And **v2.x** may be the last version support iOS 13. ## Installation diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 99307cfb..f1bcb8b8 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '2.1.0' + s.version = '2.2.0' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index 76778b66..38198777 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.1.0 + 2.2.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) From bf3d86b15d8a1f4b10341208ef07e46e240a4d06 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 23 Sep 2022 12:20:56 +0800 Subject: [PATCH 10/90] Fix the nil url always returns Error will cause infinity `onAppear` call and image manager to load, which waste CPU Instead, use url as identity to avoid this --- SDWebImageSwiftUI/Classes/WebImage.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 5e8f0bc9..680a4969 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -264,9 +264,9 @@ public struct WebImage : View { } else { result = AnyView(configure(image: .empty)) } - // UUID to avoid SwiftUI engine cache the status, and does not call `onAppear` when placeholder not changed (See `ContentView.swift/ContentView2` case) - // Because we load the image url in `onAppear`, it should be called to sync with state changes :) - return result.id(UUID()) + // Custom ID to avoid SwiftUI engine cache the status, and does not call `onAppear` when placeholder not changed (See `ContentView.swift/ContentView2` case) + // Because we load the image url in placeholder's `onAppear`, it should be called to sync with state changes :) + return result.id(imageModel.url) } } From ba9db288f42a9eda342a73158b56f0d0bd39f8fc Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 23 Sep 2022 18:18:43 +0800 Subject: [PATCH 11/90] Fix the case which sometimes the player does not stop when WebImage it out of screen This consume CPU because CADisplayLink is still running in the background --- SDWebImageSwiftUI/Classes/ImagePlayer.swift | 3 +- SDWebImageSwiftUI/Classes/WebImage.swift | 54 ++++++++++++++------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/ImagePlayer.swift b/SDWebImageSwiftUI/Classes/ImagePlayer.swift index e0ceded6..9ab55277 100644 --- a/SDWebImageSwiftUI/Classes/ImagePlayer.swift +++ b/SDWebImageSwiftUI/Classes/ImagePlayer.swift @@ -33,7 +33,6 @@ public final class ImagePlayer : ObservableObject { deinit { player?.stopPlaying() - currentFrame = nil } /// Current playing frame image @@ -57,7 +56,7 @@ public final class ImagePlayer : ObservableObject { if let player = player { return player.isPlaying && waitingPlaying } - return false + return true } /// Current playing status diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 680a4969..32c3a96c 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -118,7 +118,7 @@ public struct WebImage : View { // Load Logic setupPlaceholder() .onPlatformAppear(appear: { - setupManager() + self.setupManager() if (self.imageManager.error == nil) { // Load remote image when first appear self.imageManager.load(url: imageModel.url, options: imageModel.options, context: imageModel.context) @@ -136,6 +136,8 @@ public struct WebImage : View { } }) } + }.onDisappear { + self.disappearAction() } } @@ -205,36 +207,52 @@ public struct WebImage : View { } } + /// Animated Image Disappear should stop display link + func disappearAction() { + // Only stop the player which is not intermediate status + if !imagePlayer.isWaiting { + if self.imageConfiguration.pausable { + self.imagePlayer.pausePlaying() + } else { + self.imagePlayer.stopPlaying() + } + if self.imageConfiguration.purgeable { + self.imagePlayer.clearFrameBuffer() + } + } + } + /// Animated Image Support func setupPlayer() -> some View { - let disappearAction = { - // Only stop the player which is not intermediate status - if !imagePlayer.isWaiting { - if self.imageConfiguration.pausable { - self.imagePlayer.pausePlaying() - } else { - self.imagePlayer.stopPlaying() - } - if self.imageConfiguration.purgeable { - self.imagePlayer.clearFrameBuffer() - } - } + let shouldResetPlayer: Bool + // Image compare should use ===/!==, which is faster than isEqual: + if let animatedImage = imagePlayer.currentAnimatedImage, animatedImage !== imageManager.image! { + shouldResetPlayer = true + } else { + shouldResetPlayer = false } - if let currentFrame = imagePlayer.currentFrame, imagePlayer.currentAnimatedImage == imageManager.image! { - return configure(image: currentFrame).onPlatformAppear(appear: { + if let currentFrame = imagePlayer.currentFrame, !shouldResetPlayer { + // Bind frame index to ID to ensure onDisappear called with sync + return configure(image: currentFrame) + .id("\(imageModel.url!):\(imagePlayer.currentFrameIndex)") + .onPlatformAppear(appear: { self.imagePlayer.startPlaying() }, disappear: { disappearAction() }) } else { - return configure(image: imageManager.image!).onPlatformAppear(appear: { - self.imagePlayer.stopPlaying() - if let animatedImage = imageManager.image as? PlatformImage & SDAnimatedImageProvider { + return configure(image: imageManager.image!) + .id("\(imageModel.url!):\(imagePlayer.currentFrameIndex)") + .onPlatformAppear(appear: { + if shouldResetPlayer { // Clear previous status + self.imagePlayer.stopPlaying() self.imagePlayer.player = nil; self.imagePlayer.currentFrame = nil; self.imagePlayer.currentFrameIndex = 0; self.imagePlayer.currentLoopCount = 0; + } + if let animatedImage = imageManager.image as? PlatformImage & SDAnimatedImageProvider { self.imagePlayer.customLoopCount = self.imageConfiguration.customLoopCount self.imagePlayer.maxBufferSize = self.imageConfiguration.maxBufferSize self.imagePlayer.runLoopMode = self.imageConfiguration.runLoopMode From cd85148fc52f38f62e7f9216b7d7bab6c7b4b1b2 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 23 Sep 2022 20:08:40 +0800 Subject: [PATCH 12/90] Using the ZStack as a container to distinguish the container disappear vs frame disapppear --- SDWebImageSwiftUI/Classes/ImagePlayer.swift | 16 +++++-- SDWebImageSwiftUI/Classes/WebImage.swift | 53 +++++++++++---------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/ImagePlayer.swift b/SDWebImageSwiftUI/Classes/ImagePlayer.swift index 9ab55277..4fdc405e 100644 --- a/SDWebImageSwiftUI/Classes/ImagePlayer.swift +++ b/SDWebImageSwiftUI/Classes/ImagePlayer.swift @@ -105,11 +105,21 @@ public final class ImagePlayer : ObservableObject { currentAnimatedImage = animatedImage if let imagePlayer = SDAnimatedImagePlayer(provider: animatedImage) { imagePlayer.animationFrameHandler = { [weak self] (index, frame) in - self?.currentFrameIndex = index - self?.currentFrame = frame + guard let self = self else { + return + } + if (self.isPlaying) { + self.currentFrameIndex = index + self.currentFrame = frame + } } imagePlayer.animationLoopHandler = { [weak self] (loopCount) in - self?.currentLoopCount = loopCount + guard let self = self else { + return + } + if (self.isPlaying) { + self.currentLoopCount = loopCount + } } // Setup configuration if let maxBufferSize = maxBufferSize { diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 32c3a96c..2290182c 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -102,8 +102,17 @@ public struct WebImage : View { } public var body: some View { - return Group { - // Render Logic + // Container + return ZStack { + // This empty Image is used to receive container's level appear/disappear to start/stop player, reduce CPU usage + Image(platformImage: .empty) + .onAppear { + self.appearAction() + } + .onDisappear { + self.disappearAction() + } + // Render Logic for actual animated image frame or static image if imageManager.image != nil && imageModel.url == imageManager.currentURL { if isAnimating && !imageManager.isIncremental { setupPlayer() @@ -136,8 +145,6 @@ public struct WebImage : View { } }) } - }.onDisappear { - self.disappearAction() } } @@ -207,18 +214,20 @@ public struct WebImage : View { } } - /// Animated Image Disappear should stop display link + /// Container level to resume animation when appear + func appearAction() { + self.imagePlayer.startPlaying() + } + + /// Container level to stop animation when disappear func disappearAction() { - // Only stop the player which is not intermediate status - if !imagePlayer.isWaiting { - if self.imageConfiguration.pausable { - self.imagePlayer.pausePlaying() - } else { - self.imagePlayer.stopPlaying() - } - if self.imageConfiguration.purgeable { - self.imagePlayer.clearFrameBuffer() - } + if self.imageConfiguration.pausable { + self.imagePlayer.pausePlaying() + } else { + self.imagePlayer.stopPlaying() + } + if self.imageConfiguration.purgeable { + self.imagePlayer.clearFrameBuffer() } } @@ -235,19 +244,15 @@ public struct WebImage : View { // Bind frame index to ID to ensure onDisappear called with sync return configure(image: currentFrame) .id("\(imageModel.url!):\(imagePlayer.currentFrameIndex)") - .onPlatformAppear(appear: { - self.imagePlayer.startPlaying() - }, disappear: { - disappearAction() - }) + .onAppear {} } else { return configure(image: imageManager.image!) .id("\(imageModel.url!):\(imagePlayer.currentFrameIndex)") - .onPlatformAppear(appear: { + .onAppear { if shouldResetPlayer { // Clear previous status self.imagePlayer.stopPlaying() - self.imagePlayer.player = nil; + self.imagePlayer.player = nil self.imagePlayer.currentFrame = nil; self.imagePlayer.currentFrameIndex = 0; self.imagePlayer.currentLoopCount = 0; @@ -262,9 +267,7 @@ public struct WebImage : View { self.imagePlayer.setupPlayer(animatedImage: animatedImage) self.imagePlayer.startPlaying() } - }, disappear: { - disappearAction() - }) + } } } From 657c64af7ab86702b16d1a4cf1591de772247480 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 23 Sep 2022 21:00:07 +0800 Subject: [PATCH 13/90] Update the test case --- Tests/WebImageTests.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Tests/WebImageTests.swift b/Tests/WebImageTests.swift index fce4d24c..d51efdbc 100644 --- a/Tests/WebImageTests.swift +++ b/Tests/WebImageTests.swift @@ -23,9 +23,9 @@ class WebImageTests: XCTestCase { let imageView = WebImage(url: imageUrl) let introspectView = imageView.onSuccess { image, data, cacheType in #if os(macOS) - let displayImage = try? imageView.inspect().group().image(0).actualImage().nsImage() + let displayImage = try? imageView.inspect().zStack().image(1).actualImage().nsImage() #else - let displayImage = try? imageView.inspect().group().image(0).actualImage().cgImage() + let displayImage = try? imageView.inspect().zStack().image(1).actualImage().cgImage() #endif XCTAssertNotNil(displayImage) expectation.fulfill() @@ -47,10 +47,10 @@ class WebImageTests: XCTestCase { if let animatedImage = image as? SDAnimatedImage { XCTAssertTrue(imageView.isAnimating) #if os(macOS) - let displayImage = try? imageView.inspect().group().image(0).actualImage().nsImage() + let displayImage = try? imageView.inspect().zStack().image(1).actualImage().nsImage() let size = displayImage?.size #else - let displayImage = try? imageView.inspect().group().image(0).actualImage().cgImage() + let displayImage = try? imageView.inspect().zStack().image(1).actualImage().cgImage() let size = CGSize(width: displayImage?.width ?? 0, height: displayImage?.height ?? 0) #endif XCTAssertNotNil(displayImage) @@ -161,11 +161,11 @@ class WebImageTests: XCTestCase { let imageView = WebImage(url: imageUrl) let introspectView = imageView.onSuccess { image, data, cacheType in #if os(macOS) - let displayImage = try? imageView.inspect().group().image(0).actualImage().nsImage() + let displayImage = try? imageView.inspect().zStack().image(1).actualImage().nsImage() XCTAssertNotNil(displayImage) #else - let displayImage = try? imageView.inspect().group().image(0).actualImage().cgImage() - let orientation = try? imageView.inspect().group().image(0).actualImage().orientation() + let displayImage = try? imageView.inspect().zStack().image(1).actualImage().cgImage() + let orientation = try? imageView.inspect().zStack().image(1).actualImage().orientation() XCTAssertNotNil(displayImage) XCTAssertEqual(orientation, .leftMirrored) #endif From ed288667c909c89127ab1b690113a3e397af3098 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 23 Sep 2022 21:14:23 +0800 Subject: [PATCH 14/90] Released v2.2.1 version Update the CHANGELOG.md --- CHANGELOG.md | 10 +++++++++- SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 644c59f1..db32e694 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.2.1] - 2022-09-23 + +### Fixed + +- Fix the nil url always returns Error will cause infinity onAppear call and image manager to load, which waste CPU #235 +- Fix the case which sometimes the player does not stop when WebImage it out of screen #236 +- Al v2.2.0 users are recommended to update + ## [2.2.0] - 2022-09-22 ### Fixed - Fix iOS 13 compatibility #232 - Fix WebImage/Animated using @State to publish changes -- Al v2.1.0 users are recommend to update +- Al v2.1.0 users are recommended to update ### Changed - ImageManager API changes. The init method has no args, use `load(url:options:context:)` instead diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index f1bcb8b8..15626019 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '2.2.0' + s.version = '2.2.1' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index 38198777..adbed3f9 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.2.0 + 2.2.1 CFBundleVersion $(CURRENT_PROJECT_VERSION) From 2909b0027aa83fb2a458aeb8943a4ad5cb93f68f Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 26 Dec 2022 15:56:31 +0800 Subject: [PATCH 15/90] Drop iOS 13/macOS 10.15/tvOS 13/watchOS 6 support Changes: 1. Backport sources are removed 2. Availability is changed 3. Use StateObject instead --- Package.swift | 4 +- README.md | 24 ++- SDWebImageSwiftUI.podspec | 10 +- SDWebImageSwiftUI.xcodeproj/project.pbxproj | 66 +------- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 30 ++-- .../Classes/Backports/Backport.swift | 59 ------- .../Classes/Backports/OnChange.swift | 52 ------ .../Classes/Backports/Overlay.swift | 124 -------------- .../Classes/Backports/StateObject.swift | 151 ------------------ SDWebImageSwiftUI/Classes/Image.swift | 8 +- SDWebImageSwiftUI/Classes/ImageManager.swift | 4 +- SDWebImageSwiftUI/Classes/ImagePlayer.swift | 2 +- .../Classes/ImageViewWrapper.swift | 6 +- .../Classes/Indicator/ActivityIndicator.swift | 4 +- .../Classes/Indicator/Indicator.swift | 25 +-- .../Classes/Indicator/ProgressIndicator.swift | 4 +- .../Classes/SDWebImageSwiftUI.swift | 22 +-- .../Classes/SwiftUICompatibility.swift | 6 +- .../Classes/Transition/Transition.swift | 2 +- SDWebImageSwiftUI/Classes/WebImage.swift | 28 ++-- 20 files changed, 96 insertions(+), 535 deletions(-) delete mode 100644 SDWebImageSwiftUI/Classes/Backports/Backport.swift delete mode 100644 SDWebImageSwiftUI/Classes/Backports/OnChange.swift delete mode 100644 SDWebImageSwiftUI/Classes/Backports/Overlay.swift delete mode 100644 SDWebImageSwiftUI/Classes/Backports/StateObject.swift diff --git a/Package.swift b/Package.swift index e7c307fc..7e53f13c 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.2 +// swift-tools-version:5.3 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -6,7 +6,7 @@ import PackageDescription let package = Package( name: "SDWebImageSwiftUI", platforms: [ - .macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6) + .macOS(.v11), .iOS(.v14), .tvOS(.v14), .watchOS(.v7) ], products: [ // Products define the executables and libraries produced by a package, and make them visible to other packages. diff --git a/README.md b/README.md index d64f3c3f..4f405282 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,10 @@ All issue reports, feature requests, contributions, and GitHub stars are welcome ## Requirements + Xcode 12+ -+ iOS 13+ (14+ Recommended) -+ macOS 10.15+ (11+ Recommended) -+ tvOS 13+ (14+ Recommended) -+ watchOS 6+ (7+ Recommended) ++ iOS 14+ ++ macOS 11+ ++ tvOS 14+ ++ watchOS 7+ ## SwiftUI 2.0 Compatibility @@ -74,9 +74,7 @@ var body: some View { } ``` -Note: However, many differences behavior between iOS 13/14's is hard to fixup. And we may break some APIs (which are not designed to be public) to fixup it. - -Due to maintain issue, in the future release, we will drop the iOS 13 supports and always match SwiftUI 2.0's behavior. And **v2.x** may be the last version support iOS 13. +Note: However, many differences behavior between iOS 13/14 is hard to fixup. Due to maintain issue, from SDWebImageSwiftUI v3.0, iOS 13 is no longer supported. We always match SwiftUI 2.0's behavior. ## Installation @@ -514,7 +512,7 @@ For caches, you actually don't need to worry about anything. It just works after #### Using for backward deployment and weak linking SwiftUI -SDWebImageSwiftUI supports to use when your App Target has a deployment target version less than iOS 13/macOS 10.15/tvOS 13/watchOS 6. Which will weak linking of SwiftUI(Combine) to allows writing code with available check at runtime. +SDWebImageSwiftUI supports to use when your App Target has a deployment target version less than iOS 14/macOS 11/tvOS 14/watchOS 7. Which will weak linking of SwiftUI(Combine) to allows writing code with available check at runtime. To use backward deployment, you have to do the follow things: @@ -528,7 +526,7 @@ You should notice that all the third party SwiftUI frameworks should have this b For deployment target version below iOS 12.2 (The first version which Swift 5 Runtime bundled in iOS system), you have to change the min deployment target version of SDWebImageSwiftUI. This may take some side effect on compiler's optimization and trigger massive warnings for some frameworks. -However, for iOS 12.2+, you can still keep the min deployment target version to iOS 13, no extra warnings or performance slow down for iOS 13 client. +However, for iOS 12.2+, you can still keep the min deployment target version to iOS 14, no extra warnings or performance slow down for iOS 14 client. Because Swift use the min deployment target version to detect whether to link the App bundled Swift runtime, or the System built-in one (`/usr/lib/swift/libswiftCore.dylib`). @@ -555,7 +553,7 @@ end + For CocoaPods user, you can skip the platform version validation in Podfile with: ```ruby -platform :ios, '13.0' # This does not effect your App Target's deployment target version, just a hint for CocoaPods +platform :ios, '14.0' # This does not effect your App Target's deployment target version, just a hint for CocoaPods ``` + For SwiftPM user, SwiftPM does not support weak linking nor Library Evolution, so it can not deployment to iOS 12+ user without changing the min deployment target. @@ -568,7 +566,7 @@ Add **all the SwiftUI code** with the available annotation and runtime check, li // AppDelegate.swift func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // ... - if #available(iOS 13, *) { + if #available(iOS 14, *) { window.rootViewController = UIHostingController(rootView: ContentView()) } else { window.rootViewController = ViewController() @@ -590,11 +588,11 @@ class ViewController: UIViewController { } // ContentView.swift -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) struct ContentView : View { var body: some View { Group { - Text("Hello World iOS 13!") + Text("Hello World iOS 14!") WebImage(url: URL(string: "https://i.loli.net/2019/09/24/rX2RkVWeGKIuJvc.jpg")) } } diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 15626019..38566a06 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -21,10 +21,10 @@ It brings all your favorite features from SDWebImage, like async image loading, s.author = { 'DreamPiggy' => 'lizhuoli1126@126.com' } s.source = { :git => 'https://github.com/SDWebImage/SDWebImageSwiftUI.git', :tag => s.version.to_s } - s.ios.deployment_target = '13.0' - s.osx.deployment_target = '10.15' - s.tvos.deployment_target = '13.0' - s.watchos.deployment_target = '6.0' + s.ios.deployment_target = '14.0' + s.osx.deployment_target = '11.0' + s.tvos.deployment_target = '14.0' + s.watchos.deployment_target = '7.0' s.source_files = 'SDWebImageSwiftUI/Classes/**/*', 'SDWebImageSwiftUI/Module/*.h' s.pod_target_xcconfig = { @@ -35,5 +35,5 @@ It brings all your favorite features from SDWebImage, like async image loading, s.weak_frameworks = 'SwiftUI', 'Combine' s.dependency 'SDWebImage', '~> 5.10' - s.swift_version = '5.2' + s.swift_version = '5.3' end diff --git a/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/SDWebImageSwiftUI.xcodeproj/project.pbxproj index 1fbde810..5d053418 100644 --- a/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -31,22 +31,6 @@ 32B933E623659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; 32B933E723659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; 32B933E823659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; - 32BC087328D23D35002451BD /* StateObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC086F28D23D35002451BD /* StateObject.swift */; }; - 32BC087428D23D35002451BD /* StateObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC086F28D23D35002451BD /* StateObject.swift */; }; - 32BC087528D23D35002451BD /* StateObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC086F28D23D35002451BD /* StateObject.swift */; }; - 32BC087628D23D35002451BD /* StateObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC086F28D23D35002451BD /* StateObject.swift */; }; - 32BC087728D23D35002451BD /* OnChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC087028D23D35002451BD /* OnChange.swift */; }; - 32BC087828D23D35002451BD /* OnChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC087028D23D35002451BD /* OnChange.swift */; }; - 32BC087928D23D35002451BD /* OnChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC087028D23D35002451BD /* OnChange.swift */; }; - 32BC087A28D23D35002451BD /* OnChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC087028D23D35002451BD /* OnChange.swift */; }; - 32BC087B28D23D35002451BD /* Overlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC087128D23D35002451BD /* Overlay.swift */; }; - 32BC087C28D23D35002451BD /* Overlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC087128D23D35002451BD /* Overlay.swift */; }; - 32BC087D28D23D35002451BD /* Overlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC087128D23D35002451BD /* Overlay.swift */; }; - 32BC087E28D23D35002451BD /* Overlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC087128D23D35002451BD /* Overlay.swift */; }; - 32BC087F28D23D35002451BD /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC087228D23D35002451BD /* Backport.swift */; }; - 32BC088028D23D35002451BD /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC087228D23D35002451BD /* Backport.swift */; }; - 32BC088128D23D35002451BD /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC087228D23D35002451BD /* Backport.swift */; }; - 32BC088228D23D35002451BD /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BC087228D23D35002451BD /* Backport.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 */; }; 32C43E1622FD583700BE87F5 /* ImageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43DDC22FD54C600BE87F5 /* ImageManager.swift */; }; @@ -93,10 +77,6 @@ 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewWrapper.swift; sourceTree = ""; }; 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUICompatibility.swift; sourceTree = ""; }; 32B933E423659A1900BB7CAD /* Transition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transition.swift; sourceTree = ""; }; - 32BC086F28D23D35002451BD /* StateObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateObject.swift; sourceTree = ""; }; - 32BC087028D23D35002451BD /* OnChange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnChange.swift; sourceTree = ""; }; - 32BC087128D23D35002451BD /* Overlay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Overlay.swift; sourceTree = ""; }; - 32BC087228D23D35002451BD /* Backport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Backport.swift; sourceTree = ""; }; 32BD9C4623E03B08008D5F6A /* IndicatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndicatorTests.swift; sourceTree = ""; }; 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 = ""; }; @@ -185,17 +165,6 @@ path = Transition; sourceTree = ""; }; - 32BC086E28D23D35002451BD /* Backports */ = { - isa = PBXGroup; - children = ( - 32BC086F28D23D35002451BD /* StateObject.swift */, - 32BC087028D23D35002451BD /* OnChange.swift */, - 32BC087128D23D35002451BD /* Overlay.swift */, - 32BC087228D23D35002451BD /* Backport.swift */, - ); - path = Backports; - sourceTree = ""; - }; 32C43DC222FD540D00BE87F5 = { isa = PBXGroup; children = ( @@ -232,7 +201,6 @@ children = ( 32B933E323659A0700BB7CAD /* Transition */, 326099472362E09E006EBB22 /* Indicator */, - 32BC086E28D23D35002451BD /* Backports */, 32C43DDC22FD54C600BE87F5 /* ImageManager.swift */, 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */, 32C43DDE22FD54C600BE87F5 /* WebImage.swift */, @@ -453,11 +421,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32BC087328D23D35002451BD /* StateObject.swift in Sources */, - 32BC087728D23D35002451BD /* OnChange.swift in Sources */, 32B933E523659A1900BB7CAD /* Transition.swift in Sources */, 32CBA78025E4D7D800C6A8DC /* ImagePlayer.swift in Sources */, - 32BC087B28D23D35002451BD /* Overlay.swift in Sources */, 32C43E1722FD583700BE87F5 /* WebImage.swift in Sources */, 326B848C236335400011BDFB /* ProgressIndicator.swift in Sources */, 326B84822363350C0011BDFB /* Indicator.swift in Sources */, @@ -468,7 +433,6 @@ 32C43E1622FD583700BE87F5 /* ImageManager.swift in Sources */, 32C43E1822FD583700BE87F5 /* AnimatedImage.swift in Sources */, 32D26A022446B546005905DA /* Image.swift in Sources */, - 32BC087F28D23D35002451BD /* Backport.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -476,11 +440,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32BC087428D23D35002451BD /* StateObject.swift in Sources */, - 32BC087828D23D35002451BD /* OnChange.swift in Sources */, 32B933E623659A1900BB7CAD /* Transition.swift in Sources */, 32CBA78125E4D7D800C6A8DC /* ImagePlayer.swift in Sources */, - 32BC087C28D23D35002451BD /* Overlay.swift in Sources */, 32C43E1A22FD583700BE87F5 /* WebImage.swift in Sources */, 326B848D236335400011BDFB /* ProgressIndicator.swift in Sources */, 326B84832363350C0011BDFB /* Indicator.swift in Sources */, @@ -491,7 +452,6 @@ 32C43E1922FD583700BE87F5 /* ImageManager.swift in Sources */, 32C43E1B22FD583700BE87F5 /* AnimatedImage.swift in Sources */, 32D26A032446B546005905DA /* Image.swift in Sources */, - 32BC088028D23D35002451BD /* Backport.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -499,11 +459,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32BC087528D23D35002451BD /* StateObject.swift in Sources */, - 32BC087928D23D35002451BD /* OnChange.swift in Sources */, 32B933E723659A1900BB7CAD /* Transition.swift in Sources */, 32CBA78225E4D7D800C6A8DC /* ImagePlayer.swift in Sources */, - 32BC087D28D23D35002451BD /* Overlay.swift in Sources */, 32C43E1D22FD583800BE87F5 /* WebImage.swift in Sources */, 326B848E236335400011BDFB /* ProgressIndicator.swift in Sources */, 326B84842363350C0011BDFB /* Indicator.swift in Sources */, @@ -514,7 +471,6 @@ 32C43E1C22FD583800BE87F5 /* ImageManager.swift in Sources */, 32C43E1E22FD583800BE87F5 /* AnimatedImage.swift in Sources */, 32D26A042446B546005905DA /* Image.swift in Sources */, - 32BC088128D23D35002451BD /* Backport.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -522,11 +478,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32BC087628D23D35002451BD /* StateObject.swift in Sources */, - 32BC087A28D23D35002451BD /* OnChange.swift in Sources */, 32B933E823659A1900BB7CAD /* Transition.swift in Sources */, 32CBA78325E4D7D800C6A8DC /* ImagePlayer.swift in Sources */, - 32BC087E28D23D35002451BD /* Overlay.swift in Sources */, 32C43E2022FD583800BE87F5 /* WebImage.swift in Sources */, 326B848F236335400011BDFB /* ProgressIndicator.swift in Sources */, 326B84852363350C0011BDFB /* Indicator.swift in Sources */, @@ -537,7 +490,6 @@ 32C43E1F22FD583800BE87F5 /* ImageManager.swift in Sources */, 32C43E2122FD583800BE87F5 /* AnimatedImage.swift in Sources */, 32D26A052446B546005905DA /* Image.swift in Sources */, - 32BC088228D23D35002451BD /* Backport.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -596,8 +548,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MACOSX_DEPLOYMENT_TARGET = 10.15; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -610,10 +562,10 @@ SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TVOS_DEPLOYMENT_TARGET = 13.0; + TVOS_DEPLOYMENT_TARGET = 14.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 6.0; + WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Debug; }; @@ -663,8 +615,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MACOSX_DEPLOYMENT_TARGET = 10.15; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( @@ -676,11 +628,11 @@ SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; - TVOS_DEPLOYMENT_TARGET = 13.0; + TVOS_DEPLOYMENT_TARGET = 14.0; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 6.0; + WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Release; }; @@ -764,7 +716,6 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUI-macOS"; PRODUCT_NAME = SDWebImageSwiftUI; SDKROOT = macosx; @@ -795,7 +746,6 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUI-macOS"; PRODUCT_NAME = SDWebImageSwiftUI; SDKROOT = macosx; diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index e454432f..28357757 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -12,7 +12,7 @@ import SDWebImage #if os(iOS) || os(tvOS) || os(macOS) /// A coordinator object used for `AnimatedImage`native view bridge for UIKit/AppKit. -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public final class AnimatedImageCoordinator: NSObject { /// Any user-provided object for actual coordinator, such as delegate method, taget-action @@ -25,7 +25,7 @@ public final class AnimatedImageCoordinator: NSObject { } /// Data Binding Object, only properties in this object can support changes from user with @State and refresh -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedImageModel : ObservableObject { /// URL image @Published var url: URL? @@ -40,7 +40,7 @@ final class AnimatedImageModel : ObservableObject { } /// Loading Binding Object, only properties in this object can support changes from user with @State and refresh -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedLoadingModel : ObservableObject { @Published var image: PlatformImage? // loaded image, note when progressive loading, this will published multiple times with different partial image @Published var isLoading: Bool = false // whether network is loading or cache is querying, should only be used for indicator binding @@ -53,7 +53,7 @@ final class AnimatedLoadingModel : ObservableObject { } /// Completion Handler Binding Object, supports dynamic @State changes -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedImageHandler: ObservableObject { // Completion Handler @Published var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)? @@ -65,7 +65,7 @@ final class AnimatedImageHandler: ObservableObject { } /// Layout Binding Object, supports dynamic @State changes -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedImageLayout : ObservableObject { var contentMode: ContentMode? var aspectRatio: CGFloat? @@ -77,7 +77,7 @@ final class AnimatedImageLayout : ObservableObject { } /// Configuration Binding Object, supports dynamic @State changes -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedImageConfiguration: ObservableObject { var incrementalLoad: Bool? var maxBufferSize: UInt? @@ -99,7 +99,7 @@ final class AnimatedImageConfiguration: ObservableObject { } /// A Image View type to load image from url, data or bundle. Supports animated and static image format. -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public struct AnimatedImage : PlatformViewRepresentable { @ObservedObject var imageModel: AnimatedImageModel @ObservedObject var imageHandler = AnimatedImageHandler() @@ -566,7 +566,7 @@ public struct AnimatedImage : PlatformViewRepresentable { } // Layout -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Configurate this view's image with the specified cap insets and options. @@ -606,7 +606,7 @@ extension AnimatedImage { } // Aspect Ratio -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Constrains this view's dimensions to the specified aspect ratio. /// - Parameters: @@ -659,7 +659,7 @@ extension AnimatedImage { } // AnimatedImage Modifier -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Total loop count for animated image rendering. Defaults to nil. @@ -736,7 +736,7 @@ extension AnimatedImage { } // Completion Handler -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Provide the action when image load fails. @@ -768,7 +768,7 @@ extension AnimatedImage { } // View Coordinator Handler -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Provide the action when view representable create the native view. @@ -796,7 +796,7 @@ extension AnimatedImage { } // Web Image convenience, based on UIKit/AppKit API -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Associate a placeholder when loading image with url @@ -837,7 +837,7 @@ extension AnimatedImage { } // Indicator -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Associate a indicator when loading image with url @@ -854,7 +854,7 @@ extension AnimatedImage { } #if DEBUG -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) struct AnimatedImage_Previews : PreviewProvider { static var previews: some View { Group { diff --git a/SDWebImageSwiftUI/Classes/Backports/Backport.swift b/SDWebImageSwiftUI/Classes/Backports/Backport.swift deleted file mode 100644 index 5b0c5a45..00000000 --- a/SDWebImageSwiftUI/Classes/Backports/Backport.swift +++ /dev/null @@ -1,59 +0,0 @@ -import SwiftUI -import ObjectiveC - -/// Provides a convenient method for backporting API, -/// including types, functions, properties, property wrappers and more. -/// -/// To backport a SwiftUI Label for example, you could apply the -/// following extension: -/// -/// extension Backport where Content == Any { -/// public struct Label { } -/// } -/// -/// Now if we want to provide further extensions to our backport type, -/// we need to ensure we retain the `Content == Any` generic requirement: -/// -/// extension Backport.Label where Content == Any, Title == Text, Icon == Image { -/// public init(_ title: S, systemName: String) { } -/// } -/// -/// In addition to types, we can also provide backports for properties -/// and methods: -/// -/// extension Backport.Label where Content: View { -/// func onChange(of value: Value, perform action: (Value) -> Void) -> some View { -/// // `content` provides access to the extended type -/// content.modifier(OnChangeModifier(value, action)) -/// } -/// } -/// -public struct Backport { - - /// The underlying content this backport represents. - public let content: Wrapped - - /// Initializes a new Backport for the specified content. - /// - Parameter content: The content (type) that's being backported - public init(_ content: Wrapped) { - self.content = content - } - -} - -public extension View { - /// Wraps a SwiftUI `View` that can be extended to provide backport functionality. - var backport: Backport { .init(self) } -} - -public extension NSObjectProtocol { - /// Wraps an `NSObject` that can be extended to provide backport functionality. - var backport: Backport { .init(self) } -} - -public extension AnyTransition { - /// Wraps an `AnyTransition` that can be extended to provide backport functionality. - static var backport: Backport{ - Backport(.identity) - } -} diff --git a/SDWebImageSwiftUI/Classes/Backports/OnChange.swift b/SDWebImageSwiftUI/Classes/Backports/OnChange.swift deleted file mode 100644 index c53eaf77..00000000 --- a/SDWebImageSwiftUI/Classes/Backports/OnChange.swift +++ /dev/null @@ -1,52 +0,0 @@ -import SwiftUI -import Combine - -@available(iOS, deprecated: 14.0) -@available(macOS, deprecated: 11.0) -@available(tvOS, deprecated: 14.0) -@available(watchOS, deprecated: 7.0) -public extension Backport where Wrapped: View { - - /// Adds a modifier for this view that fires an action when a specific - /// value changes. - /// - /// `onChange` is called on the main thread. Avoid performing long-running - /// tasks on the main thread. If you need to perform a long-running task in - /// response to `value` changing, you should dispatch to a background queue. - /// - /// The new value is passed into the closure. - /// - /// - Parameters: - /// - value: The value to observe for changes - /// - action: A closure to run when the value changes. - /// - newValue: The new value that changed - /// - /// - Returns: A view that fires an action when the specified value changes. - @ViewBuilder - func onChange(of value: Value, perform action: @escaping (Value) -> Void) -> some View { - content.modifier(ChangeModifier(value: value, action: action)) - } - -} - -private struct ChangeModifier: ViewModifier { - let value: Value - let action: (Value) -> Void - - @State var oldValue: Value? - - init(value: Value, action: @escaping (Value) -> Void) { - self.value = value - self.action = action - _oldValue = .init(initialValue: value) - } - - func body(content: Content) -> some View { - content - .onReceive(Just(value)) { newValue in - guard newValue != oldValue else { return } - action(newValue) - oldValue = newValue - } - } -} diff --git a/SDWebImageSwiftUI/Classes/Backports/Overlay.swift b/SDWebImageSwiftUI/Classes/Backports/Overlay.swift deleted file mode 100644 index a6f1a1ff..00000000 --- a/SDWebImageSwiftUI/Classes/Backports/Overlay.swift +++ /dev/null @@ -1,124 +0,0 @@ -import SwiftUI - -public extension Backport where Wrapped: View { - - /// Layers the views that you specify in front of this view. - /// - /// Use this modifier to place one or more views in front of another view. - /// For example, you can place a group of stars on a ``RoundedRectangle``: - /// - /// RoundedRectangle(cornerRadius: 8) - /// .frame(width: 200, height: 100) - /// .overlay(alignment: .topLeading) { Star(color: .red) } - /// .overlay(alignment: .topTrailing) { Star(color: .yellow) } - /// .overlay(alignment: .bottomLeading) { Star(color: .green) } - /// .overlay(alignment: .bottomTrailing) { Star(color: .blue) } - /// - /// The example above assumes that you've defined a `Star` view with a - /// parameterized color: - /// - /// struct Star: View { - /// var color = Color.yellow - /// - /// var body: some View { - /// Image(systemName: "star.fill") - /// .foregroundStyle(color) - /// } - /// } - /// - /// By setting different `alignment` values for each modifier, you make the - /// stars appear in different places on the rectangle: - /// - /// ![A screenshot of a rounded rectangle with a star in each corner. The - /// star in the upper-left is red; the start in the upper-right is yellow; - /// the star in the lower-left is green; the star the lower-right is - /// blue.](View-overlay-2) - /// - /// If you specify more than one view in the `content` closure, the modifier - /// collects all of the views in the closure into an implicit ``ZStack``, - /// taking them in order from back to front. For example, you can place a - /// star and a ``Circle`` on a field of ``ShapeStyle/blue``: - /// - /// Color.blue - /// .frame(width: 200, height: 200) - /// .overlay { - /// Circle() - /// .frame(width: 100, height: 100) - /// Star() - /// } - /// - /// Both the overlay modifier and the implicit ``ZStack`` composed from the - /// overlay content --- the circle and the star --- use a default - /// ``Alignment/center`` alignment. The star appears centered on the circle, - /// and both appear as a composite view centered in front of the square: - /// - /// ![A screenshot of a star centered on a circle, which is - /// centered on a square.](View-overlay-3) - /// - /// If you specify an alignment for the overlay, it applies to the implicit - /// stack rather than to the individual views in the closure. You can see - /// this if you add the ``Alignment/bottom`` alignment: - /// - /// Color.blue - /// .frame(width: 200, height: 200) - /// .overlay(alignment: .bottom) { - /// Circle() - /// .frame(width: 100, height: 100) - /// Star() - /// } - /// - /// The circle and the star move down as a unit to align the stack's bottom - /// edge with the bottom edge of the square, while the star remains - /// centered on the circle: - /// - /// ![A screenshot of a star centered on a circle, which is on a square. - /// The circle's bottom edge is aligned with the square's bottom - /// edge.](View-overlay-3a) - /// - /// To control the placement of individual items inside the `content` - /// closure, either use a different overlay modifier for each item, as the - /// earlier example of stars in the corners of a rectangle demonstrates, or - /// add an explicit ``ZStack`` inside the content closure with its own - /// alignment: - /// - /// Color.blue - /// .frame(width: 200, height: 200) - /// .overlay(alignment: .bottom) { - /// ZStack(alignment: .bottom) { - /// Circle() - /// .frame(width: 100, height: 100) - /// Star() - /// } - /// } - /// - /// The stack alignment ensures that the star's bottom edge aligns with the - /// circle's, while the overlay aligns the composite view with the square: - /// - /// ![A screenshot of a star, a circle, and a square with all their - /// bottom edges aligned.](View-overlay-4) - /// - /// You can achieve layering without an overlay modifier by putting both the - /// modified view and the overlay content into a ``ZStack``. This can - /// produce a simpler view hierarchy, but changes the layout priority that - /// SwiftUI applies to the views. Use the overlay modifier when you want the - /// modified view to dominate the layout. - /// - /// If you want to specify a ``ShapeStyle`` like a ``Color`` or a - /// ``Material`` as the overlay, use - /// ``View/overlay(_:ignoresSafeAreaEdges:)`` instead. To specify a - /// ``Shape``, use ``View/overlay(_:in:fillStyle:)``. - /// - /// - Parameters: - /// - alignment: The alignment that the modifier uses to position the - /// implicit ``ZStack`` that groups the foreground views. The default - /// is ``Alignment/center``. - /// - content: A ``ViewBuilder`` that you use to declare the views to - /// draw in front of this view, stacked in the order that you list them. - /// The last view that you list appears at the front of the stack. - /// - /// - Returns: A view that uses the specified content as a foreground. - func overlay(alignment: Alignment = .center, @ViewBuilder _ content: () -> Content) -> some View { - self.content.overlay(content(), alignment: alignment) - } - -} diff --git a/SDWebImageSwiftUI/Classes/Backports/StateObject.swift b/SDWebImageSwiftUI/Classes/Backports/StateObject.swift deleted file mode 100644 index 5d47b2ba..00000000 --- a/SDWebImageSwiftUI/Classes/Backports/StateObject.swift +++ /dev/null @@ -1,151 +0,0 @@ -import Combine -import SwiftUI - -@available(iOS, deprecated: 14.0) -@available(macOS, deprecated: 11.0) -@available(tvOS, deprecated: 14.0) -@available(watchOS, deprecated: 7.0) -public extension Backport where Wrapped: ObservableObject { - - /// A property wrapper type that instantiates an observable object. - /// - /// Create a state object in a ``SwiftUI/View``, ``SwiftUI/App``, or - /// ``SwiftUI/Scene`` by applying the `@Backport.StateObject` attribute to a property - /// declaration and providing an initial value that conforms to the - /// - /// protocol: - /// - /// @Backport.StateObject var model = DataModel() - /// - /// SwiftUI creates a new instance of the object only once for each instance of - /// the structure that declares the object. When published properties of the - /// observable object change, SwiftUI updates the parts of any view that depend - /// on those properties: - /// - /// Text(model.title) // Updates the view any time `title` changes. - /// - /// You can pass the state object into a property that has the - /// ``SwiftUI/ObservedObject`` attribute. You can alternatively add the object - /// to the environment of a view hierarchy by applying the - /// ``SwiftUI/View/environmentObject(_:)`` modifier: - /// - /// ContentView() - /// .environmentObject(model) - /// - /// If you create an environment object as shown in the code above, you can - /// read the object inside `ContentView` or any of its descendants - /// using the ``SwiftUI/EnvironmentObject`` attribute: - /// - /// @EnvironmentObject var model: DataModel - /// - /// Get a ``SwiftUI/Binding`` to one of the state object's properties using the - /// `$` operator. Use a binding when you want to create a two-way connection to - /// one of the object's properties. For example, you can let a - /// ``SwiftUI/Toggle`` control a Boolean value called `isEnabled` stored in the - /// model: - /// - /// Toggle("Enabled", isOn: $model.isEnabled) - @propertyWrapper struct StateObject: DynamicProperty { - private final class Wrapper: ObservableObject { - private var subject = PassthroughSubject() - - var value: Wrapped? { - didSet { - cancellable = nil - cancellable = value?.objectWillChange - .sink { [subject] _ in subject.send() } - } - } - - private var cancellable: AnyCancellable? - - var objectWillChange: AnyPublisher { - subject.eraseToAnyPublisher() - } - } - - @State private var state = Wrapper() - - @ObservedObject private var observedObject = Wrapper() - - private var thunk: () -> Wrapped - - /// The underlying value referenced by the state object. - /// - /// The wrapped value property provides primary access to the value's data. - /// However, you don't access `wrappedValue` directly. Instead, use the - /// property variable created with the `@Backport.StateObject` attribute: - /// - /// @Backport.StateObject var contact = Contact() - /// - /// var body: some View { - /// Text(contact.name) // Accesses contact's wrapped value. - /// } - /// - /// When you change a property of the wrapped value, you can access the new - /// value immediately. However, SwiftUI updates views displaying the value - /// asynchronously, so the user interface might not update immediately. - public var wrappedValue: Wrapped { - if let object = state.value { - return object - } else { - let object = thunk() - state.value = object - return object - } - } - - /// A projection of the state object that creates bindings to its - /// properties. - /// - /// Use the projected value to pass a binding value down a view hierarchy. - /// To get the projected value, prefix the property variable with `$`. For - /// example, you can get a binding to a model's `isEnabled` Boolean so that - /// a ``SwiftUI/Toggle`` view can control the value: - /// - /// struct MyView: View { - /// @Backport.StateObject var model = DataModel() - /// - /// var body: some View { - /// Toggle("Enabled", isOn: $model.isEnabled) - /// } - /// } - public var projectedValue: ObservedObject.Wrapper { - ObservedObject(wrappedValue: wrappedValue).projectedValue - } - - /// Creates a new state object with an initial wrapped value. - /// - /// You don’t call this initializer directly. Instead, declare a property - /// with the `@Backport.StateObject` attribute in a ``SwiftUI/View``, - /// ``SwiftUI/App``, or ``SwiftUI/Scene``, and provide an initial value: - /// - /// struct MyView: View { - /// @Backport.StateObject var model = DataModel() - /// - /// // ... - /// } - /// - /// SwiftUI creates only one instance of the state object for each - /// container instance that you declare. In the code above, SwiftUI - /// creates `model` only the first time it initializes a particular instance - /// of `MyView`. On the other hand, each different instance of `MyView` - /// receives a distinct copy of the data model. - /// - /// - Parameter thunk: An initial value for the state object. - public init(wrappedValue thunk: @autoclosure @escaping () -> Wrapped) { - self.thunk = thunk - } - - public mutating func update() { - if state.value == nil { - state.value = thunk() - } - if observedObject.value !== state.value { - observedObject.value = state.value - } - } - } - -} - diff --git a/SDWebImageSwiftUI/Classes/Image.swift b/SDWebImageSwiftUI/Classes/Image.swift index 28ba9697..4dd60adf 100644 --- a/SDWebImageSwiftUI/Classes/Image.swift +++ b/SDWebImageSwiftUI/Classes/Image.swift @@ -9,7 +9,7 @@ import Foundation import SwiftUI -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension Image { @inlinable init(platformImage: PlatformImage) { #if os(macOS) @@ -20,13 +20,13 @@ extension Image { } } -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension PlatformImage { static var empty = PlatformImage() } #if os(iOS) || os(tvOS) || os(watchOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension PlatformImage.Orientation { @inlinable var toSwiftUI: Image.Orientation { switch self { @@ -52,7 +52,7 @@ extension PlatformImage.Orientation { } } -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension Image.Orientation { @inlinable var toPlatform: PlatformImage.Orientation { switch self { diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index d9bce8fa..2c134ce7 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -11,7 +11,7 @@ import SDWebImage /// A Image observable object for handle image load process. This drive the Source of Truth for image loading status. /// You can use `@ObservedObject` to associate each instance of manager to your View type, which update your view's body from SwiftUI framework when image was loaded. -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public final class ImageManager : ObservableObject { /// loaded image, note when progressive loading, this will published multiple times with different partial image @Published public var image: PlatformImage? @@ -107,7 +107,7 @@ public final class ImageManager : ObservableObject { } // Completion Handler -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension ImageManager { /// Provide the action when image load fails. /// - Parameters: diff --git a/SDWebImageSwiftUI/Classes/ImagePlayer.swift b/SDWebImageSwiftUI/Classes/ImagePlayer.swift index 4fdc405e..4f0f1a8e 100644 --- a/SDWebImageSwiftUI/Classes/ImagePlayer.swift +++ b/SDWebImageSwiftUI/Classes/ImagePlayer.swift @@ -10,7 +10,7 @@ import SwiftUI import SDWebImage /// A Image observable object for handle aniamted image playback. This is used to avoid `@State` update may capture the View struct type and cause memory leak. -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public final class ImagePlayer : ObservableObject { var player: SDAnimatedImagePlayer? diff --git a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift index 80f936ee..e13e92ae 100644 --- a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift +++ b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift @@ -12,7 +12,7 @@ import SDWebImage #if os(iOS) || os(tvOS) || os(macOS) /// Use wrapper to solve tne `UIImageView`/`NSImageView` frame size become image size issue (SwiftUI's Bug) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public class AnimatedImageViewWrapper : PlatformView { var wrapped = SDAnimatedImageView() var interpolationQuality = CGInterpolationQuality.default @@ -67,7 +67,7 @@ public class AnimatedImageViewWrapper : PlatformView { } /// Use wrapper to solve the `UIProgressView`/`NSProgressIndicator` frame origin NaN crash (SwiftUI's bug) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public class ProgressIndicatorWrapper : PlatformView { #if os(macOS) var wrapped = NSProgressIndicator() @@ -98,7 +98,7 @@ public class ProgressIndicatorWrapper : PlatformView { } } -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) 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. diff --git a/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift b/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift index 7103c77e..2592ebd5 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift @@ -10,7 +10,7 @@ import SwiftUI #if os(macOS) || os(iOS) || os(tvOS) /// An activity indicator (system style) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public struct ActivityIndicator: PlatformViewRepresentable { @Binding var isAnimating: Bool var style: Style @@ -72,7 +72,7 @@ public struct ActivityIndicator: PlatformViewRepresentable { #endif } -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension ActivityIndicator { public enum Style { case medium diff --git a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift index 37eb9030..69fbb4d0 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift @@ -10,7 +10,7 @@ import Foundation import SwiftUI /// A type to build the indicator -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public struct Indicator where T : View { var content: (Binding, Binding) -> T @@ -25,7 +25,7 @@ public struct Indicator where T : View { } /// A observable model to report indicator loading status -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public class IndicatorStatus : ObservableObject { /// whether indicator is loading or not @Published var isLoading: Bool = false @@ -36,7 +36,7 @@ public class IndicatorStatus : ObservableObject { /// A implementation detail View Modifier with indicator /// SwiftUI View Modifier construced by using a internal View type which modify the `body` /// It use type system to represent the view hierarchy, and Swift `some View` syntax to hide the type detail for users -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public struct IndicatorViewModifier : ViewModifier where T : View { /// The loading status @@ -45,21 +45,22 @@ public struct IndicatorViewModifier : ViewModifier where T : View { /// The indicator public var indicator: Indicator + @ViewBuilder + private var overlay: some View { + if status.isLoading { + indicator.content($status.isLoading, $status.progress) + } + } + public func body(content: Content) -> some View { ZStack { - content - .backport - .overlay { - if status.isLoading { - indicator.content($status.isLoading, $status.progress) - } - } + content.overlay(overlay, alignment: .center) } } } #if os(macOS) || os(iOS) || os(tvOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension Indicator where T == ActivityIndicator { /// Activity Indicator public static var activity: Indicator { @@ -77,7 +78,7 @@ extension Indicator where T == ActivityIndicator { } } -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension Indicator where T == ProgressIndicator { /// Progress Indicator public static var progress: Indicator { diff --git a/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift b/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift index 1256ee87..a0170666 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift @@ -10,7 +10,7 @@ import SwiftUI #if os(macOS) || os(iOS) || os(tvOS) /// A progress bar indicator (system style) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public struct ProgressIndicator: PlatformViewRepresentable { @Binding var isAnimating: Bool @Binding var progress: Double @@ -102,7 +102,7 @@ public struct ProgressIndicator: PlatformViewRepresentable { #endif } -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension ProgressIndicator { public enum Style { case `default` diff --git a/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift b/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift index 9c0e71ad..45ae3c27 100644 --- a/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift +++ b/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift @@ -11,53 +11,53 @@ import SwiftUI @_exported import SDWebImage // Automatically import SDWebImage #if os(macOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformImage = NSImage #else -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformImage = UIImage #endif #if os(macOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformView = NSView #endif #if os(iOS) || os(tvOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformView = UIView #endif #if os(watchOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformView = WKInterfaceObject #endif #if os(macOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformViewRepresentable = NSViewRepresentable #endif #if os(iOS) || os(tvOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformViewRepresentable = UIViewRepresentable #endif #if os(watchOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformViewRepresentable = WKInterfaceObjectRepresentable #endif #if os(macOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension NSViewRepresentable { typealias PlatformViewType = NSViewType } #endif #if os(iOS) || os(tvOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension UIViewRepresentable { typealias PlatformViewType = UIViewType } #endif #if os(watchOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension WKInterfaceObjectRepresentable { typealias PlatformViewType = WKInterfaceObjectType } diff --git a/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift b/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift index e7a46393..1e9f66c3 100644 --- a/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift +++ b/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift @@ -11,7 +11,7 @@ import SwiftUI #if os(iOS) || os(tvOS) || os(macOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) struct PlatformAppear: PlatformViewRepresentable { let appearAction: () -> Void let disappearAction: () -> Void @@ -38,7 +38,7 @@ struct PlatformAppear: PlatformViewRepresentable { #endif } -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) class PlatformAppearView: PlatformView { var appearAction: () -> Void = {} var disappearAction: () -> Void = {} @@ -74,7 +74,7 @@ class PlatformAppearView: PlatformView { #endif -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension View { /// Used UIKit/AppKit behavior to detect the SwiftUI view's visibility. /// This hack is because of SwiftUI 1.0/2.0 buggy behavior. The built-in `onAppear` and `onDisappear` is so massive on some cases. Where UIKit/AppKit is solid. diff --git a/SDWebImageSwiftUI/Classes/Transition/Transition.swift b/SDWebImageSwiftUI/Classes/Transition/Transition.swift index c4a908f2..e42503c5 100644 --- a/SDWebImageSwiftUI/Classes/Transition/Transition.swift +++ b/SDWebImageSwiftUI/Classes/Transition/Transition.swift @@ -8,7 +8,7 @@ import SwiftUI -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension AnyTransition { /// Fade-in transition diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 2290182c..faaa423f 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -10,7 +10,7 @@ import SwiftUI import SDWebImage /// Data Binding Object, only properties in this object can support changes from user with @State and refresh -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) final class WebImageModel : ObservableObject { /// URL image @Published var url: URL? @@ -19,7 +19,7 @@ final class WebImageModel : ObservableObject { } /// Completion Handler Binding Object, supports dynamic @State changes -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) final class WebImageHandler: ObservableObject { // Completion Handler @Published var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)? @@ -28,7 +28,7 @@ final class WebImageHandler: ObservableObject { } /// Configuration Binding Object, supports dynamic @State changes -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) final class WebImageConfiguration: ObservableObject { var retryOnAppear: Bool = true var cancelOnDisappear: Bool = true @@ -42,7 +42,7 @@ final class WebImageConfiguration: ObservableObject { } /// A Image View type to load image from url. Supports static/animated image format. -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public struct WebImage : View { var configurations: [(Image) -> Image] = [] @@ -63,11 +63,9 @@ public struct WebImage : View { @ObservedObject var indicatorStatus : IndicatorStatus - // FIXME: Use SwiftUI StateObject and remove onPlatformAppear once drop iOS 13 support - @Backport.StateObject var imagePlayer = ImagePlayer() + @StateObject var imagePlayer = ImagePlayer() - // FIXME: Use SwiftUI StateObject and remove onPlatformAppear once drop iOS 13 support - @Backport.StateObject var imageManager : ImageManager + @StateObject var imageManager : ImageManager /// Create a web image with url, placeholder, custom options and context. Optional can support animated image using Binding. /// - Parameter url: The image url @@ -89,7 +87,7 @@ public struct WebImage : View { imageModel.context = context _imageModel = ObservedObject(wrappedValue: imageModel) let imageManager = ImageManager() - _imageManager = Backport.StateObject(wrappedValue: imageManager) + _imageManager = StateObject(wrappedValue: imageManager) _indicatorStatus = ObservedObject(wrappedValue: imageManager.indicatorStatus) } @@ -292,7 +290,7 @@ public struct WebImage : View { } // Layout -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { func configure(_ block: @escaping (Image) -> Image) -> WebImage { var result = self @@ -330,7 +328,7 @@ extension WebImage { } // Completion Handler -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { /// Provide the action when image load fails. @@ -362,7 +360,7 @@ extension WebImage { } // WebImage Modifier -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { /// Associate a placeholder when loading image with url @@ -401,7 +399,7 @@ extension WebImage { } // Indicator -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { /// Associate a indicator when loading image with url @@ -418,7 +416,7 @@ extension WebImage { } // Animated Image -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { /// Total loop count for animated image rendering. Defaults to nil. @@ -486,7 +484,7 @@ extension WebImage { } #if DEBUG -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) struct WebImage_Previews : PreviewProvider { static var previews: some View { Group { From 63e1aebbf6dc3d435fc345460c70e816fd2016da Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 26 Dec 2022 17:46:33 +0800 Subject: [PATCH 16/90] Remove the legacy ActivityIndicator/ProgressIndicator, use ProrgessView --- ...eSwiftUIDemo-watchOS WatchKit App.xcscheme | 25 +--- .../SDWebImageSwiftUIDemo/ContentView.swift | 17 --- .../Classes/Indicator/ActivityIndicator.swift | 82 ------------- .../Classes/Indicator/Indicator.swift | 25 ++-- .../Classes/Indicator/ProgressIndicator.swift | 114 ------------------ 5 files changed, 18 insertions(+), 245 deletions(-) delete mode 100644 SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift delete mode 100644 SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift diff --git a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme index 3d1f081f..65df20bc 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme +++ b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme @@ -54,10 +54,8 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> - + - + - + - - - - - + diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index a3619c90..418bad7a 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -17,23 +17,6 @@ class UserSettings: ObservableObject { #endif } -#if os(watchOS) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -extension Indicator where T == ProgressView { - static var activity: Indicator { - Indicator { isAnimating, progress in - ProgressView() - } - } - - static var progress: Indicator { - Indicator { isAnimating, progress in - ProgressView(value: progress.wrappedValue) - } - } -} -#endif - // Test Switching url using @State struct ContentView2: View { @State var imageURLs = [ diff --git a/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift b/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift deleted file mode 100644 index 2592ebd5..00000000 --- a/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift +++ /dev/null @@ -1,82 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -import SwiftUI - -#if os(macOS) || os(iOS) || os(tvOS) -/// An activity indicator (system style) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -public struct ActivityIndicator: PlatformViewRepresentable { - @Binding var isAnimating: Bool - var style: Style - - /// Create the indicator with animation binding and style - /// - Parameters: - /// - isAnimating: The binding to control the animation - /// - style: The indicator style - public init(_ isAnimating: Binding, style: Style = .medium) { - self._isAnimating = isAnimating - self.style = style - } - - #if os(macOS) - public typealias NSViewType = NSProgressIndicator - #elseif os(iOS) || os(tvOS) - public typealias UIViewType = UIActivityIndicatorView - #endif - - #if os(iOS) || os(tvOS) - public func makeUIView(context: UIViewRepresentableContext) -> UIActivityIndicatorView { - let activityStyle: UIActivityIndicatorView.Style - switch style { - case .medium: - activityStyle = .medium - case .large: - activityStyle = .large - } - let indicator = UIActivityIndicatorView(style: activityStyle) - indicator.hidesWhenStopped = true - return indicator - } - - public func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext) { - isAnimating ? uiView.startAnimating() : uiView.stopAnimating() - } - #endif - - #if os(macOS) - public func makeNSView(context: NSViewRepresentableContext) -> NSProgressIndicator { - let controlSize: NSControl.ControlSize - switch style { - case .medium: - controlSize = .small - case .large: - controlSize = .regular - } - let indicator = NSProgressIndicator() - indicator.style = .spinning - indicator.controlSize = controlSize - indicator.isDisplayedWhenStopped = false - return indicator - } - - public func updateNSView(_ nsView: NSProgressIndicator, context: NSViewRepresentableContext) { - isAnimating ? nsView.startAnimation(nil) : nsView.stopAnimation(nil) - } - - #endif -} - -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -extension ActivityIndicator { - public enum Style { - case medium - case large - } -} -#endif diff --git a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift index 69fbb4d0..499aafd9 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift @@ -54,45 +54,44 @@ public struct IndicatorViewModifier : ViewModifier where T : View { public func body(content: Content) -> some View { ZStack { - content.overlay(overlay, alignment: .center) + content + overlay } } } -#if os(macOS) || os(iOS) || os(tvOS) @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -extension Indicator where T == ActivityIndicator { +extension Indicator where T == AnyView { /// Activity Indicator - public static var activity: Indicator { + public static var activity: Indicator { Indicator { isAnimating, _ in - ActivityIndicator(isAnimating) + AnyView(ProgressView().opacity(isAnimating.wrappedValue ? 1 : 0)) } } /// Activity Indicator with style /// - Parameter style: style - public static func activity(style: ActivityIndicator.Style) -> Indicator { + public static func activity(style: any ProgressViewStyle) -> Indicator { Indicator { isAnimating, _ in - ActivityIndicator(isAnimating, style: style) + AnyView(ProgressView().progressViewStyle(style).opacity(isAnimating.wrappedValue ? 1 : 0)) } } } @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -extension Indicator where T == ProgressIndicator { +extension Indicator where T == AnyView { /// Progress Indicator - public static var progress: Indicator { + public static var progress: Indicator { Indicator { isAnimating, progress in - ProgressIndicator(isAnimating, progress: progress) + AnyView(ProgressView(value: progress.wrappedValue).opacity(isAnimating.wrappedValue ? 1 : 0)) } } /// Progress Indicator with style /// - Parameter style: style - public static func progress(style: ProgressIndicator.Style) -> Indicator { + public static func progress(style: any ProgressViewStyle) -> Indicator { Indicator { isAnimating, progress in - ProgressIndicator(isAnimating, progress: progress, style: style) + AnyView(ProgressView(value: progress.wrappedValue).progressViewStyle(style).opacity(isAnimating.wrappedValue ? 1 : 0)) } } } -#endif diff --git a/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift b/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift deleted file mode 100644 index a0170666..00000000 --- a/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift +++ /dev/null @@ -1,114 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -import SwiftUI - -#if os(macOS) || os(iOS) || os(tvOS) -/// A progress bar indicator (system style) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -public struct ProgressIndicator: PlatformViewRepresentable { - @Binding var isAnimating: Bool - @Binding var progress: Double - var style: Style - - /// Create indicator with animation binding, progress binding and the style - /// - Parameters: - /// - isAnimating: The binding to control the animation - /// - progress: The binding to update the progress - /// - style: The indicator style - public init(_ isAnimating: Binding, progress: Binding, style: Style = .default) { - self._isAnimating = isAnimating - self._progress = progress - self.style = style - } - - #if os(macOS) - public typealias NSViewType = ProgressIndicatorWrapper - #elseif os(iOS) || os(tvOS) - public typealias UIViewType = ProgressIndicatorWrapper - #endif - - #if os(iOS) || os(tvOS) - public func makeUIView(context: UIViewRepresentableContext) -> ProgressIndicatorWrapper { - let progressStyle: UIProgressView.Style - switch style { - #if os(iOS) - case .bar: - progressStyle = .bar - #endif - case .default: - progressStyle = .default - } - let uiView = ProgressIndicatorWrapper() - let view = uiView.wrapped - view.progressViewStyle = progressStyle - return uiView - } - - public func updateUIView(_ uiView: ProgressIndicatorWrapper, context: UIViewRepresentableContext) { - let view = uiView.wrapped - if isAnimating { - view.setProgress(Float(progress), animated: true) - } else { - if progress == 0 { - view.isHidden = false - view.progress = 0 - } else { - view.isHidden = true - view.progress = 1 - } - } - } - #endif - - #if os(macOS) - public func makeNSView(context: NSViewRepresentableContext) -> ProgressIndicatorWrapper { - let nsView = ProgressIndicatorWrapper() - let view = nsView.wrapped - view.style = .bar - view.isDisplayedWhenStopped = false - view.controlSize = .small - view.frame = CGRect(x: 0, y: 0, width: 160, height: 0) // Width from `UIProgressView` default width - view.sizeToFit() - view.autoresizingMask = [.maxXMargin, .minXMargin, .maxYMargin, .minYMargin] - return nsView - } - - public func updateNSView(_ nsView: ProgressIndicatorWrapper, context: NSViewRepresentableContext) { - let view = nsView.wrapped - if isAnimating { - view.isIndeterminate = false - view.doubleValue = Double(progress) * 100 - view.startAnimation(nil) - } else { - if progress == 0 { - view.isHidden = false - view.isIndeterminate = true - view.doubleValue = 0 - view.stopAnimation(nil) - } else { - view.isHidden = true - view.isIndeterminate = false - view.doubleValue = 100 - view.stopAnimation(nil) - } - } - } - #endif -} - -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -extension ProgressIndicator { - public enum Style { - case `default` - #if os(iOS) - case bar - #endif - } -} -#endif From f2a7b990841c44bd88f8bb01c1de5ff6556ecb64 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 26 Dec 2022 18:04:02 +0800 Subject: [PATCH 17/90] Use the generic signature for ProgressiveStyle This match Apple's API --- Example/SDWebImageSwiftUIDemo/DetailView.swift | 2 +- SDWebImageSwiftUI/Classes/Indicator/Indicator.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Example/SDWebImageSwiftUIDemo/DetailView.swift b/Example/SDWebImageSwiftUIDemo/DetailView.swift index f9e7dea0..1c2b723f 100644 --- a/Example/SDWebImageSwiftUIDemo/DetailView.swift +++ b/Example/SDWebImageSwiftUIDemo/DetailView.swift @@ -111,7 +111,7 @@ struct DetailView: View { WebImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating) .resizable() .placeholder(.wifiExclamationmark) - .indicator(.progress) + .indicator(.progress(style: .circular)) .scaledToFit() } } diff --git a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift index 499aafd9..76113ced 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift @@ -71,7 +71,7 @@ extension Indicator where T == AnyView { /// Activity Indicator with style /// - Parameter style: style - public static func activity(style: any ProgressViewStyle) -> Indicator { + public static func activity(style: S) -> Indicator where S: ProgressViewStyle { Indicator { isAnimating, _ in AnyView(ProgressView().progressViewStyle(style).opacity(isAnimating.wrappedValue ? 1 : 0)) } @@ -89,7 +89,7 @@ extension Indicator where T == AnyView { /// Progress Indicator with style /// - Parameter style: style - public static func progress(style: any ProgressViewStyle) -> Indicator { + public static func progress(style: S) -> Indicator where S: ProgressViewStyle { Indicator { isAnimating, progress in AnyView(ProgressView(value: progress.wrappedValue).progressViewStyle(style).opacity(isAnimating.wrappedValue ? 1 : 0)) } From 2b2ee4f671d17470b8fedff99e5ca2431a133349 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 26 Dec 2022 18:07:45 +0800 Subject: [PATCH 18/90] Remove the unused ProgressIndicatorWrapper --- .../Classes/ImageViewWrapper.swift | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift index e13e92ae..88a35498 100644 --- a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift +++ b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift @@ -66,38 +66,6 @@ public class AnimatedImageViewWrapper : PlatformView { } } -/// Use wrapper to solve the `UIProgressView`/`NSProgressIndicator` frame origin NaN crash (SwiftUI's bug) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -public class ProgressIndicatorWrapper : PlatformView { - #if os(macOS) - var wrapped = NSProgressIndicator() - #else - var wrapped = UIProgressView(progressViewStyle: .default) - #endif - - #if os(macOS) - public override func layout() { - super.layout() - wrapped.setFrameOrigin(CGPoint(x: round(self.bounds.width - wrapped.frame.width) / 2, y: round(self.bounds.height - wrapped.frame.height) / 2)) - } - #else - public override func layoutSubviews() { - super.layoutSubviews() - wrapped.center = self.center - } - #endif - - public override init(frame frameRect: CGRect) { - super.init(frame: frameRect) - addSubview(wrapped) - } - - public required init?(coder: NSCoder) { - super.init(coder: coder) - addSubview(wrapped) - } -} - @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension PlatformView { /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview. From 8192aecee6ba3e11919687a8c318850455a66fa8 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 26 Dec 2022 18:25:16 +0800 Subject: [PATCH 19/90] Remove the unused HostingView hack --- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 15 --------------- SDWebImageSwiftUI/Classes/ImageViewWrapper.swift | 14 -------------- 2 files changed, 29 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 28357757..73aa2239 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -261,21 +261,6 @@ public struct AnimatedImage : PlatformViewRepresentable { } self.imageHandler.progressBlock?(receivedSize, expectedSize) }) { (image, data, error, cacheType, finished, _) in - if #available(iOS 14.0, macOS 11.0, watchOS 7.0, tvOS 14.0, *) { - // Do nothing. on iOS 14's SwiftUI, the @Published will always trigger another `updateUIView` call with new UIView instance. - } else { - // This is a hack because of iOS 13's SwiftUI bug, the @Published does not trigger another `updateUIView` call - // Here I have to use UIKit/AppKit API to triger the same effect (the window change implicitly cause re-render) - if let hostingView = view.findHostingView() { - if let _ = hostingView.window { - #if os(macOS) - hostingView.viewDidMoveToWindow() - #else - hostingView.didMoveToWindow() - #endif - } - } - } context.coordinator.imageLoading.image = image context.coordinator.imageLoading.isLoading = false context.coordinator.imageLoading.progress = 1 diff --git a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift index 88a35498..f49aa1a4 100644 --- a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift +++ b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift @@ -82,20 +82,6 @@ extension PlatformView { self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 0).isActive = true self.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: 0).isActive = true } - - /// Finding the HostingView for UIKit/AppKit View. - /// - Parameter entry: The entry platform view - /// - Returns: The hosting view. - func findHostingView() -> PlatformView? { - var superview = self.superview - while let s = superview { - if NSStringFromClass(type(of: s)).contains("HostingView") { - return s - } - superview = s.superview - } - return nil - } } #endif From 940907a6f93e56b5976618f5ebffc4a5a68a1545 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 26 Dec 2022 19:13:33 +0800 Subject: [PATCH 20/90] Fix the bug that isAnimating control does not works on WebImage --- SDWebImageSwiftUI/Classes/ImagePlayer.swift | 16 ---------------- SDWebImageSwiftUI/Classes/WebImage.swift | 19 ++++++++++++++----- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/ImagePlayer.swift b/SDWebImageSwiftUI/Classes/ImagePlayer.swift index 4fdc405e..4e61e820 100644 --- a/SDWebImageSwiftUI/Classes/ImagePlayer.swift +++ b/SDWebImageSwiftUI/Classes/ImagePlayer.swift @@ -14,8 +14,6 @@ import SDWebImage public final class ImagePlayer : ObservableObject { var player: SDAnimatedImagePlayer? - var waitingPlaying = false - /// Max buffer size public var maxBufferSize: UInt? @@ -51,14 +49,6 @@ public final class ImagePlayer : ObservableObject { player != nil } - /// The player is preparing to resume from previous stop state. This is intermediate status when previous frame disappear and new frame appear - public var isWaiting: Bool { - if let player = player { - return player.isPlaying && waitingPlaying - } - return true - } - /// Current playing status public var isPlaying: Bool { player?.isPlaying ?? false @@ -67,12 +57,6 @@ public final class ImagePlayer : ObservableObject { /// Start the animation public func startPlaying() { player?.startPlaying() - waitingPlaying = true - DispatchQueue.main.async { - // This workaround `WebImage` caller - // Which previous frame onDisappear and new frame onAppear, cause player status wrong - self.waitingPlaying = false - } } /// Pause the animation diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 2290182c..65fdf15e 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -117,11 +117,7 @@ public struct WebImage : View { if isAnimating && !imageManager.isIncremental { setupPlayer() } else { - if let currentFrame = imagePlayer.currentFrame { - configure(image: currentFrame) - } else { - configure(image: imageManager.image!) - } + displayImage() } } else { // Load Logic @@ -231,6 +227,16 @@ public struct WebImage : View { } } + /// Static Image Display + func displayImage() -> some View { + disappearAction() + if let currentFrame = imagePlayer.currentFrame { + return configure(image: currentFrame) + } else { + return configure(image: imageManager.image!) + } + } + /// Animated Image Support func setupPlayer() -> some View { let shouldResetPlayer: Bool @@ -240,6 +246,9 @@ public struct WebImage : View { } else { shouldResetPlayer = false } + if !shouldResetPlayer { + imagePlayer.startPlaying() + } if let currentFrame = imagePlayer.currentFrame, !shouldResetPlayer { // Bind frame index to ID to ensure onDisappear called with sync return configure(image: currentFrame) From 84e792704bb3c8e71086ae40f84401609cee7145 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 26 Dec 2022 19:17:20 +0800 Subject: [PATCH 21/90] Removed the unused IndicatorTests --- .../project.pbxproj | 8 -- Tests/IndicatorTests.swift | 84 ------------------- 2 files changed, 92 deletions(-) delete mode 100644 Tests/IndicatorTests.swift diff --git a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj index c370771a..044416f3 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -15,19 +15,16 @@ 320CDC3222FADB45007CF858 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3122FADB45007CF858 /* Assets.xcassets */; }; 320CDC3522FADB45007CF858 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3422FADB45007CF858 /* Preview Assets.xcassets */; }; 320CDC3822FADB45007CF858 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3622FADB45007CF858 /* LaunchScreen.storyboard */; }; - 322E0DF728D331A20003A55F /* IndicatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF028D331A20003A55F /* IndicatorTests.swift */; }; 322E0DF828D331A20003A55F /* WebImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF128D331A20003A55F /* WebImageTests.swift */; }; 322E0DF928D331A20003A55F /* Images.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 322E0DF228D331A20003A55F /* Images.bundle */; }; 322E0DFA28D331A20003A55F /* ImageManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF328D331A20003A55F /* ImageManagerTests.swift */; }; 322E0DFB28D331A20003A55F /* AnimatedImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF428D331A20003A55F /* AnimatedImageTests.swift */; }; 322E0DFD28D331A20003A55F /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF628D331A20003A55F /* TestUtils.swift */; }; 322E0E1828D3320D0003A55F /* ImageManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF328D331A20003A55F /* ImageManagerTests.swift */; }; - 322E0E1928D3320D0003A55F /* IndicatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF028D331A20003A55F /* IndicatorTests.swift */; }; 322E0E1A28D3320D0003A55F /* WebImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF128D331A20003A55F /* WebImageTests.swift */; }; 322E0E1B28D3320D0003A55F /* AnimatedImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF428D331A20003A55F /* AnimatedImageTests.swift */; }; 322E0E1C28D3320D0003A55F /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF628D331A20003A55F /* TestUtils.swift */; }; 322E0E1D28D3320D0003A55F /* ImageManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF328D331A20003A55F /* ImageManagerTests.swift */; }; - 322E0E1E28D3320D0003A55F /* IndicatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF028D331A20003A55F /* IndicatorTests.swift */; }; 322E0E1F28D3320D0003A55F /* WebImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF128D331A20003A55F /* WebImageTests.swift */; }; 322E0E2028D3320D0003A55F /* AnimatedImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF428D331A20003A55F /* AnimatedImageTests.swift */; }; 322E0E2128D3320D0003A55F /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF628D331A20003A55F /* TestUtils.swift */; }; @@ -142,7 +139,6 @@ 320CDC3722FADB45007CF858 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 320CDC3922FADB45007CF858 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 322E0DE628D3318B0003A55F /* SDWebImageSwiftUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SDWebImageSwiftUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 322E0DF028D331A20003A55F /* IndicatorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IndicatorTests.swift; sourceTree = ""; }; 322E0DF128D331A20003A55F /* WebImageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebImageTests.swift; sourceTree = ""; }; 322E0DF228D331A20003A55F /* Images.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Images.bundle; sourceTree = ""; }; 322E0DF328D331A20003A55F /* ImageManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageManagerTests.swift; sourceTree = ""; }; @@ -323,7 +319,6 @@ 322E0DEF28D331A20003A55F /* Tests */ = { isa = PBXGroup; children = ( - 322E0DF028D331A20003A55F /* IndicatorTests.swift */, 322E0DF128D331A20003A55F /* WebImageTests.swift */, 322E0DF228D331A20003A55F /* Images.bundle */, 322E0DF328D331A20003A55F /* ImageManagerTests.swift */, @@ -1159,7 +1154,6 @@ 322E0DF828D331A20003A55F /* WebImageTests.swift in Sources */, 322E0DFD28D331A20003A55F /* TestUtils.swift in Sources */, 322E0DFB28D331A20003A55F /* AnimatedImageTests.swift in Sources */, - 322E0DF728D331A20003A55F /* IndicatorTests.swift in Sources */, 322E0DFA28D331A20003A55F /* ImageManagerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1172,7 +1166,6 @@ 322E0E1A28D3320D0003A55F /* WebImageTests.swift in Sources */, 322E0E1828D3320D0003A55F /* ImageManagerTests.swift in Sources */, 322E0E1C28D3320D0003A55F /* TestUtils.swift in Sources */, - 322E0E1928D3320D0003A55F /* IndicatorTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1184,7 +1177,6 @@ 322E0E1F28D3320D0003A55F /* WebImageTests.swift in Sources */, 322E0E1D28D3320D0003A55F /* ImageManagerTests.swift in Sources */, 322E0E2128D3320D0003A55F /* TestUtils.swift in Sources */, - 322E0E1E28D3320D0003A55F /* IndicatorTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Tests/IndicatorTests.swift b/Tests/IndicatorTests.swift deleted file mode 100644 index cef4ae4c..00000000 --- a/Tests/IndicatorTests.swift +++ /dev/null @@ -1,84 +0,0 @@ -import XCTest -import SwiftUI -import ViewInspector -@testable import SDWebImageSwiftUI - -extension ActivityIndicator : Inspectable {} -extension ProgressIndicator : Inspectable {} - -#if os(iOS) || os(tvOS) -typealias ActivityIndicatorViewType = UIActivityIndicatorView -typealias ProgressIndicatorViewType = UIProgressView -#else -typealias ActivityIndicatorViewType = NSProgressIndicator -typealias ProgressIndicatorViewType = NSProgressIndicator -#endif - -class IndicatorTests: XCTestCase { - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - SDImageCache.shared.clear(with: .all) - } - - func testActivityIndicator() throws { - let expectation = self.expectation(description: "Activity indicator") - let binding = Binding(wrappedValue: true) - let indicator = ActivityIndicator(binding, style: .medium) - ViewHosting.host(view: indicator) - let indicatorView = try indicator.inspect().actualView().platformView() - #if os(iOS) || os(tvOS) - XCTAssertTrue(indicatorView.isAnimating) - #endif - binding.wrappedValue = false - XCTAssertFalse(binding.wrappedValue) - XCTAssertFalse(indicator.isAnimating) - #if os(iOS) || os(tvOS) - indicatorView.stopAnimating() - #else - indicatorView.stopAnimation(nil) - #endif - #if os(iOS) || os(tvOS) - XCTAssertFalse(indicatorView.isAnimating) - #endif - expectation.fulfill() - self.waitForExpectations(timeout: 5, handler: nil) - ViewHosting.expel() - } - - func testProgressIndicator() throws { - let expectation = self.expectation(description: "Progress indicator") - let binding = Binding(wrappedValue: true) - let progress = Binding(wrappedValue: 0) - let indicator = ProgressIndicator(binding, progress: progress) - ViewHosting.host(view: indicator) - let indicatorView = try indicator.inspect().actualView().platformView().wrapped - #if os(iOS) || os(tvOS) - XCTAssertEqual(indicatorView.progress, 0.0) - #else - XCTAssertEqual(indicatorView.doubleValue, 0.0) - #endif - progress.wrappedValue = 1.0 - XCTAssertEqual(indicator.progress, 1.0) - #if os(iOS) || os(tvOS) - indicatorView.setProgress(1.0, animated: true) - #else - indicatorView.increment(by: 1.0) - #endif - #if os(iOS) || os(tvOS) - XCTAssertEqual(indicatorView.progress, 1.0) - #else - XCTAssertEqual(indicatorView.doubleValue, 1.0) - #endif - expectation.fulfill() - self.waitForExpectations(timeout: 5, handler: nil) - ViewHosting.expel() - } - -} From 7db63eda0ace5ed15f7f0f5d66cd89f61078968a Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 26 Dec 2022 19:20:05 +0800 Subject: [PATCH 22/90] Update README --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4f405282..4fbeeba8 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,14 @@ var body: some View { } ``` -Note: For indicator, you can custom your own as well. For example, iOS 14/watchOS 7 introduce the new `ProgressView`, which can replace our built-in `ProgressIndicator/ActivityIndicator` (where watchOS does not provide). +Note: For indicator, you can custom your own as well. For example, iOS 14/watchOS 7 introduce the new `ProgressView`, which can be easily used via: + +```swift +WebImage(url: url) +.indicator(.activity) +``` + +or you can just write like: ```swift WebImage(url: url) @@ -628,7 +635,7 @@ SDWebImageSwiftUI has Unit Test to increase code quality. For SwiftUI, there are However, since SwiftUI is State-Based and Attributed-Implemented layout system, there are open source projects who provide the solution: -+ [ViewInspector](https://github.com/nalexn/ViewInspector): Inspect View's runtime attribute value (like `.frame` modifier, `.image` value). We use this to test `AnimatedImage` and `WebImage`. It also allows the inspect to native UIView/NSView, which we use to test `ActivityIndicator` and `ProgressIndicator`. ++ [ViewInspector](https://github.com/nalexn/ViewInspector): Inspect View's runtime attribute value (like `.frame` modifier, `.image` value). We use this to test `AnimatedImage` and `WebImage`. It also allows the inspect to native UIView/NSView. To run the test: From 61fefe9c284fd41ddef77d02749e88f00c305196 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Tue, 27 Dec 2022 20:10:39 +0800 Subject: [PATCH 23/90] Released v2.2.2 version Update the CHANGELOG.md --- CHANGELOG.md | 6 ++++++ SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db32e694..ee36f238 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.2.2] - 2022-12-27 + +### Fixed +- Fix the bug that isAnimating control does not works on WebImage #251 +- Note you should upgrade the SDWebImage 5.14.3+, or this may cause extra Xcode 14's runtime warning (function is unaffected) + ## [2.2.1] - 2022-09-23 ### Fixed diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 15626019..35a172a0 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '2.2.1' + s.version = '2.2.2' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index adbed3f9..7a03f492 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.2.1 + 2.2.2 CFBundleVersion $(CURRENT_PROJECT_VERSION) From 029aa85ea47b17513cf83a72261dd38124799138 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sun, 23 Apr 2023 21:31:19 +0800 Subject: [PATCH 24/90] Fix the issue that Static Library + Library Evolution cause the build issue on Swift 5.8 For CocoaPods user, they can use Static Library and we should not touch the xcconfig here --- SDWebImageSwiftUI.podspec | 1 - 1 file changed, 1 deletion(-) diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 35a172a0..d5a0e3bc 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -30,7 +30,6 @@ It brings all your favorite features from SDWebImage, like async image loading, s.pod_target_xcconfig = { 'SUPPORTS_MACCATALYST' => 'YES', 'DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER' => 'NO', - 'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES' } s.weak_frameworks = 'SwiftUI', 'Combine' From e837c37d45449fbd3b4745c10c5b5274e73edead Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sun, 23 Apr 2023 23:25:10 +0800 Subject: [PATCH 25/90] Released v2.2.3 version Update the CHANGELOG.md --- CHANGELOG.md | 3 +++ SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee36f238..7a81c8f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.2.3] - 2023-04-32 +- Fix the issue that Static Library + Library Evolution cause the build issue on Swift 5.8 #263 + ## [2.2.2] - 2022-12-27 ### Fixed diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index d5a0e3bc..0c83570e 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '2.2.2' + s.version = '2.2.3' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index 7a03f492..49c664c0 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.2.2 + 2.2.3 CFBundleVersion $(CURRENT_PROJECT_VERSION) From 2d4839a195b5fadd61a741a330fb41620c66c459 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 22 Jun 2023 19:20:01 +0800 Subject: [PATCH 26/90] Added visionOS support Add visionOS Demo The scale using traitCollection.displayScale --- .../project.pbxproj | 721 +++++++----------- .../contents.xcworkspacedata | 2 +- .../SDWebImageSwiftUIDemo-visionOS.xcscheme | 77 ++ ...eSwiftUIDemo-watchOS WatchKit App.xcscheme | 25 +- .../AppDelegate.swift | 41 + .../Content.imageset/Contents.json | 12 + .../Back.solidimagestacklayer/Contents.json | 6 + .../AppIcon.solidimagestack/Contents.json | 17 + .../Content.imageset/Contents.json | 12 + .../Front.solidimagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 12 + .../Middle.solidimagestacklayer/Contents.json | 6 + .../Assets.xcassets/Contents.json | 6 + .../SDWebImageSwiftUIDemo-visionOS/Info.plist | 11 + .../Preview Assets.xcassets/Contents.json | 6 + .../SDWebImageSwiftUIDemo/ContentView.swift | 16 +- .../SDWebImageSwiftUIDemo/DetailView.swift | 6 +- SDWebImageSwiftUI.xcodeproj/project.pbxproj | 2 - .../contents.xcworkspacedata | 3 - SDWebImageSwiftUI/Classes/AnimatedImage.swift | 16 +- SDWebImageSwiftUI/Classes/Image.swift | 2 +- .../Classes/ImageViewWrapper.swift | 2 +- .../Classes/Indicator/ActivityIndicator.swift | 6 +- .../Classes/Indicator/Indicator.swift | 2 +- .../Classes/Indicator/ProgressIndicator.swift | 6 +- .../Classes/SDWebImageSwiftUI.swift | 6 +- .../Classes/SwiftUICompatibility.swift | 4 +- SDWebImageSwiftUI/Classes/WebImage.swift | 12 +- 28 files changed, 537 insertions(+), 506 deletions(-) create mode 100644 Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-visionOS.xcscheme create mode 100644 Example/SDWebImageSwiftUIDemo-visionOS/AppDelegate.swift create mode 100644 Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json create mode 100644 Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json create mode 100644 Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Contents.json create mode 100644 Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json create mode 100644 Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json create mode 100644 Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json create mode 100644 Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json create mode 100644 Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/Contents.json create mode 100644 Example/SDWebImageSwiftUIDemo-visionOS/Info.plist create mode 100644 Example/SDWebImageSwiftUIDemo-visionOS/Preview Content/Preview Assets.xcassets/Contents.json diff --git a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj index c370771a..7f76fcef 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -3,12 +3,10 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ - 1794840F9DF6D50ADA448C9B /* Pods_SDWebImageSwiftUIDemo_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 473D7886C23B6FC5AFE35842 /* Pods_SDWebImageSwiftUIDemo_macOS.framework */; }; - 2E3D81A12C757E01A3C420F2 /* Pods_SDWebImageSwiftUITests_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83FA763A8587C065798A274B /* Pods_SDWebImageSwiftUITests_tvOS.framework */; }; 320CDC2C22FADB44007CF858 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2B22FADB44007CF858 /* AppDelegate.swift */; }; 320CDC2E22FADB44007CF858 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2D22FADB44007CF858 /* SceneDelegate.swift */; }; 320CDC3022FADB44007CF858 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; }; @@ -34,9 +32,21 @@ 322E0E2228D332130003A55F /* Images.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 322E0DF228D331A20003A55F /* Images.bundle */; }; 322E0E2328D332130003A55F /* Images.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 322E0DF228D331A20003A55F /* Images.bundle */; }; 326B0D712345C01900D28269 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; - 327B90F228DC4EBB003E8BD9 /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 327B90F128DC4EBB003E8BD9 /* ViewInspector */; }; - 327B90F428DC4EC0003E8BD9 /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 327B90F328DC4EC0003E8BD9 /* ViewInspector */; }; - 32DCFE9528D333E8001A17BF /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 32DCFE9428D333E8001A17BF /* ViewInspector */; }; + 32B13E812AA368B700BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E802AA368B700BE9B5B /* SDWebImageSwiftUI */; }; + 32B13E832AA368B900BE9B5B /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E822AA368B900BE9B5B /* SDWebImage */; }; + 32B13E852AA368C600BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E842AA368C600BE9B5B /* SDWebImageSwiftUI */; }; + 32B13E872AA368C900BE9B5B /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E862AA368C900BE9B5B /* SDWebImage */; }; + 32B13E892AA368CC00BE9B5B /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E882AA368CC00BE9B5B /* SDWebImage */; }; + 32B13E8F2AA368E100BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E8E2AA368E100BE9B5B /* SDWebImageSwiftUI */; }; + 32B13E912AA368E300BE9B5B /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E902AA368E300BE9B5B /* SDWebImage */; }; + 32B13E932AA368EF00BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E922AA368EF00BE9B5B /* SDWebImageSwiftUI */; }; + 32B13E952AA368F300BE9B5B /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E942AA368F300BE9B5B /* SDWebImage */; }; + 32D5D1672A445B260098BDFC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5D1662A445B260098BDFC /* AppDelegate.swift */; }; + 32D5D16B2A445B260098BDFC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32D5D16A2A445B260098BDFC /* Assets.xcassets */; }; + 32D5D16E2A445B260098BDFC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32D5D16D2A445B260098BDFC /* Preview Assets.xcassets */; }; + 32D5D1722A445BF00098BDFC /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; + 32D5D1732A445BF00098BDFC /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; }; + 32D5D1762A445C8F0098BDFC /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32D5D1752A445C8F0098BDFC /* SDWebImageSwiftUI */; }; 32E5290C2348A0C700EA46FF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E5290B2348A0C700EA46FF /* AppDelegate.swift */; }; 32E529102348A0C900EA46FF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32E5290F2348A0C900EA46FF /* Assets.xcassets */; }; 32E529132348A0C900EA46FF /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32E529122348A0C900EA46FF /* Preview Assets.xcassets */; }; @@ -59,11 +69,6 @@ 32E529662348A10B00EA46FF /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; 32E529682348A10C00EA46FF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; }; 32E529692348A10C00EA46FF /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; - 68543C9252A5BD46E9573195 /* Pods_SDWebImageSwiftUIDemo_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79C3538209F8065DCCFBE205 /* Pods_SDWebImageSwiftUIDemo_tvOS.framework */; }; - 833A61715BAAB31702D867CC /* Pods_SDWebImageSwiftUITests_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1A272DB5547C37A41C1238E /* Pods_SDWebImageSwiftUITests_macOS.framework */; }; - 8E29022B4DCBF0EFF9CF82F9 /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E25DB0256669F3B7EE7C566D /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework */; }; - 9E3892775FC4E348DFA66FCA /* Pods_SDWebImageSwiftUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FEDED19F84B1D678B12077A /* Pods_SDWebImageSwiftUITests.framework */; }; - E61581A5A1063B0E6795157D /* Pods_SDWebImageSwiftUIDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0FCDD95C695D2F914DC9B3B /* Pods_SDWebImageSwiftUIDemo.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -130,9 +135,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 0EEE88A04A9B191BD966EFC2 /* Pods-SDWebImageSwiftUITests macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUITests macOS.release.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUITests macOS/Pods-SDWebImageSwiftUITests macOS.release.xcconfig"; sourceTree = ""; }; 28546D27CDA9666E64C183FD /* SDWebImageSwiftUI.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = SDWebImageSwiftUI.podspec; path = ../SDWebImageSwiftUI.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - 2FEDED19F84B1D678B12077A /* Pods_SDWebImageSwiftUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 320CDC2922FADB44007CF858 /* SDWebImageSwiftUIDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SDWebImageSwiftUIDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 320CDC2B22FADB44007CF858 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 320CDC2D22FADB44007CF858 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -152,6 +155,13 @@ 322E0E0228D331F00003A55F /* SDWebImageSwiftUITests macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SDWebImageSwiftUITests macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 322E0E0F28D332050003A55F /* SDWebImageSwiftUITests tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SDWebImageSwiftUITests tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 326B0D702345C01900D28269 /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = ""; }; + 3294617D2AA36759009E391B /* SDWebImage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SDWebImage; path = ../../SDWebImage; sourceTree = ""; }; + 3294617E2AA36761009E391B /* SDWebImageSwiftUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SDWebImageSwiftUI; path = ..; sourceTree = ""; }; + 32D5D1602A445B250098BDFC /* SDWebImageSwiftUIDemo-visionOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SDWebImageSwiftUIDemo-visionOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 32D5D1662A445B260098BDFC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; wrapsLines = 0; }; + 32D5D16A2A445B260098BDFC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 32D5D16D2A445B260098BDFC /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 32D5D17F2A4463170098BDFC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 32E529092348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SDWebImageSwiftUIDemo-macOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 32E5290B2348A0C700EA46FF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 32E5290F2348A0C900EA46FF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -177,26 +187,7 @@ 32E529542348A0DF00EA46FF /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 32E529562348A0DF00EA46FF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3E9F8B5F06960FFFBD1A5F99 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; - 473D7886C23B6FC5AFE35842 /* Pods_SDWebImageSwiftUIDemo_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUIDemo_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 487B2863C76EC4CE36CEC4EA /* Pods-SDWebImageSwiftUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUITests.debug.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUITests/Pods-SDWebImageSwiftUITests.debug.xcconfig"; sourceTree = ""; }; 54859B427E0A79E823713963 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; - 5864FFEDE62A0630EDF26A56 /* Pods-SDWebImageSwiftUIDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo.release.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUIDemo/Pods-SDWebImageSwiftUIDemo.release.xcconfig"; sourceTree = ""; }; - 5ABE9344AF344CCC70056CD5 /* Pods-SDWebImageSwiftUITests tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUITests tvOS.debug.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUITests tvOS/Pods-SDWebImageSwiftUITests tvOS.debug.xcconfig"; sourceTree = ""; }; - 746AF60263F54FD7E16AA7D5 /* Pods-SDWebImageSwiftUIDemo-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo-tvOS.debug.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUIDemo-tvOS/Pods-SDWebImageSwiftUIDemo-tvOS.debug.xcconfig"; sourceTree = ""; }; - 79C3538209F8065DCCFBE205 /* Pods_SDWebImageSwiftUIDemo_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUIDemo_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7B0D9182CAD02B73E6F208F3 /* Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.release.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.release.xcconfig"; sourceTree = ""; }; - 83FA763A8587C065798A274B /* Pods_SDWebImageSwiftUITests_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUITests_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 89B11BBDBAA86F760DF1EE2D /* Pods-SDWebImageSwiftUIDemo-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo-macOS.debug.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUIDemo-macOS/Pods-SDWebImageSwiftUIDemo-macOS.debug.xcconfig"; sourceTree = ""; }; - 95C9E0D9CE4113E5A82480B9 /* Pods-SDWebImageSwiftUIDemo-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo-tvOS.release.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUIDemo-tvOS/Pods-SDWebImageSwiftUIDemo-tvOS.release.xcconfig"; sourceTree = ""; }; - A78BA7FB5AFF53CBDD4C4CBD /* Pods-SDWebImageSwiftUIDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo.debug.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUIDemo/Pods-SDWebImageSwiftUIDemo.debug.xcconfig"; sourceTree = ""; }; - A7CD2F7825F1936052B2C65E /* Pods-SDWebImageSwiftUITests macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUITests macOS.debug.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUITests macOS/Pods-SDWebImageSwiftUITests macOS.debug.xcconfig"; sourceTree = ""; }; - B6E12746E84E9ED7FA910A24 /* Pods-SDWebImageSwiftUITests tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUITests tvOS.release.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUITests tvOS/Pods-SDWebImageSwiftUITests tvOS.release.xcconfig"; sourceTree = ""; }; - C1A272DB5547C37A41C1238E /* Pods_SDWebImageSwiftUITests_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUITests_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - DDE527DE0EF6B6D9B7CD8C97 /* Pods-SDWebImageSwiftUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUITests.release.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUITests/Pods-SDWebImageSwiftUITests.release.xcconfig"; sourceTree = ""; }; - E25DB0256669F3B7EE7C566D /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F0FCDD95C695D2F914DC9B3B /* Pods_SDWebImageSwiftUIDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUIDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F3AACDC116F5598BC39A8573 /* Pods-SDWebImageSwiftUIDemo-macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo-macOS.release.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUIDemo-macOS/Pods-SDWebImageSwiftUIDemo-macOS.release.xcconfig"; sourceTree = ""; }; - FEED4964309E241D2FD8A544 /* Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.debug.xcconfig"; path = "../../Pods/Target Support Files/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -204,7 +195,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E61581A5A1063B0E6795157D /* Pods_SDWebImageSwiftUIDemo.framework in Frameworks */, + 32B13E892AA368CC00BE9B5B /* SDWebImage in Frameworks */, + 32D5D1762A445C8F0098BDFC /* SDWebImageSwiftUI in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -212,8 +204,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9E3892775FC4E348DFA66FCA /* Pods_SDWebImageSwiftUITests.framework in Frameworks */, - 32DCFE9528D333E8001A17BF /* ViewInspector in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -221,8 +211,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 833A61715BAAB31702D867CC /* Pods_SDWebImageSwiftUITests_macOS.framework in Frameworks */, - 327B90F228DC4EBB003E8BD9 /* ViewInspector in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -230,8 +218,15 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2E3D81A12C757E01A3C420F2 /* Pods_SDWebImageSwiftUITests_tvOS.framework in Frameworks */, - 327B90F428DC4EC0003E8BD9 /* ViewInspector in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 32D5D15D2A445B250098BDFC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 32B13E832AA368B900BE9B5B /* SDWebImage in Frameworks */, + 32B13E812AA368B700BE9B5B /* SDWebImageSwiftUI in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -239,7 +234,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1794840F9DF6D50ADA448C9B /* Pods_SDWebImageSwiftUIDemo_macOS.framework in Frameworks */, + 32B13E872AA368C900BE9B5B /* SDWebImage in Frameworks */, + 32B13E852AA368C600BE9B5B /* SDWebImageSwiftUI in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -247,7 +243,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 68543C9252A5BD46E9573195 /* Pods_SDWebImageSwiftUIDemo_tvOS.framework in Frameworks */, + 32B13E912AA368E300BE9B5B /* SDWebImage in Frameworks */, + 32B13E8F2AA368E100BE9B5B /* SDWebImageSwiftUI in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -255,7 +252,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8E29022B4DCBF0EFF9CF82F9 /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -270,33 +266,14 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 32B13E952AA368F300BE9B5B /* SDWebImage in Frameworks */, + 32B13E932AA368EF00BE9B5B /* SDWebImageSwiftUI in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 1DEE67F18F512F2F2F78E283 /* Pods */ = { - isa = PBXGroup; - children = ( - A78BA7FB5AFF53CBDD4C4CBD /* Pods-SDWebImageSwiftUIDemo.debug.xcconfig */, - 5864FFEDE62A0630EDF26A56 /* Pods-SDWebImageSwiftUIDemo.release.xcconfig */, - 89B11BBDBAA86F760DF1EE2D /* Pods-SDWebImageSwiftUIDemo-macOS.debug.xcconfig */, - F3AACDC116F5598BC39A8573 /* Pods-SDWebImageSwiftUIDemo-macOS.release.xcconfig */, - 746AF60263F54FD7E16AA7D5 /* Pods-SDWebImageSwiftUIDemo-tvOS.debug.xcconfig */, - 95C9E0D9CE4113E5A82480B9 /* Pods-SDWebImageSwiftUIDemo-tvOS.release.xcconfig */, - FEED4964309E241D2FD8A544 /* Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.debug.xcconfig */, - 7B0D9182CAD02B73E6F208F3 /* Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.release.xcconfig */, - 487B2863C76EC4CE36CEC4EA /* Pods-SDWebImageSwiftUITests.debug.xcconfig */, - DDE527DE0EF6B6D9B7CD8C97 /* Pods-SDWebImageSwiftUITests.release.xcconfig */, - A7CD2F7825F1936052B2C65E /* Pods-SDWebImageSwiftUITests macOS.debug.xcconfig */, - 0EEE88A04A9B191BD966EFC2 /* Pods-SDWebImageSwiftUITests macOS.release.xcconfig */, - 5ABE9344AF344CCC70056CD5 /* Pods-SDWebImageSwiftUITests tvOS.debug.xcconfig */, - B6E12746E84E9ED7FA910A24 /* Pods-SDWebImageSwiftUITests tvOS.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; 320CDC2A22FADB44007CF858 /* SDWebImageSwiftUIDemo */ = { isa = PBXGroup; children = ( @@ -335,6 +312,32 @@ path = ../Tests; sourceTree = ""; }; + 32D5D1612A445B260098BDFC /* SDWebImageSwiftUIDemo-visionOS */ = { + isa = PBXGroup; + children = ( + 32D5D17F2A4463170098BDFC /* Info.plist */, + 32D5D1662A445B260098BDFC /* AppDelegate.swift */, + 32D5D16A2A445B260098BDFC /* Assets.xcassets */, + 32D5D16C2A445B260098BDFC /* Preview Content */, + ); + path = "SDWebImageSwiftUIDemo-visionOS"; + sourceTree = ""; + }; + 32D5D16C2A445B260098BDFC /* Preview Content */ = { + isa = PBXGroup; + children = ( + 32D5D16D2A445B260098BDFC /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 32D5D17A2A445D220098BDFC /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; 32E5290A2348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS */ = { isa = PBXGroup; children = ( @@ -409,16 +412,18 @@ 607FACC71AFB9204008FA782 = { isa = PBXGroup; children = ( + 3294617D2AA36759009E391B /* SDWebImage */, + 3294617E2AA36761009E391B /* SDWebImageSwiftUI */, 607FACF51AFB993E008FA782 /* Podspec Metadata */, 320CDC2A22FADB44007CF858 /* SDWebImageSwiftUIDemo */, 32E5290A2348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS */, 32E529212348A0D300EA46FF /* SDWebImageSwiftUIDemo-tvOS */, 32E5293B2348A0DD00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit App */, 32E5294A2348A0DE00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit Extension */, + 32D5D1612A445B260098BDFC /* SDWebImageSwiftUIDemo-visionOS */, 322E0DEF28D331A20003A55F /* Tests */, 607FACD11AFB9204008FA782 /* Products */, - 1DEE67F18F512F2F2F78E283 /* Pods */, - F1EB66AFCE0A1C6D551D02DD /* Frameworks */, + 32D5D17A2A445D220098BDFC /* Frameworks */, ); sourceTree = ""; }; @@ -434,6 +439,7 @@ 322E0DE628D3318B0003A55F /* SDWebImageSwiftUITests.xctest */, 322E0E0228D331F00003A55F /* SDWebImageSwiftUITests macOS.xctest */, 322E0E0F28D332050003A55F /* SDWebImageSwiftUITests tvOS.xctest */, + 32D5D1602A445B250098BDFC /* SDWebImageSwiftUIDemo-visionOS.app */, ); name = Products; sourceTree = ""; @@ -448,20 +454,6 @@ name = "Podspec Metadata"; sourceTree = ""; }; - F1EB66AFCE0A1C6D551D02DD /* Frameworks */ = { - isa = PBXGroup; - children = ( - F0FCDD95C695D2F914DC9B3B /* Pods_SDWebImageSwiftUIDemo.framework */, - 473D7886C23B6FC5AFE35842 /* Pods_SDWebImageSwiftUIDemo_macOS.framework */, - 79C3538209F8065DCCFBE205 /* Pods_SDWebImageSwiftUIDemo_tvOS.framework */, - E25DB0256669F3B7EE7C566D /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework */, - 2FEDED19F84B1D678B12077A /* Pods_SDWebImageSwiftUITests.framework */, - C1A272DB5547C37A41C1238E /* Pods_SDWebImageSwiftUITests_macOS.framework */, - 83FA763A8587C065798A274B /* Pods_SDWebImageSwiftUITests_tvOS.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -469,17 +461,19 @@ isa = PBXNativeTarget; buildConfigurationList = 320CDC3C22FADB45007CF858 /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo" */; buildPhases = ( - 58D483FABAB44B4EC2E538D0 /* [CP] Check Pods Manifest.lock */, 320CDC2522FADB44007CF858 /* Sources */, 320CDC2622FADB44007CF858 /* Frameworks */, 320CDC2722FADB44007CF858 /* Resources */, - 0B5ABDA8213E875CE5FCC890 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = SDWebImageSwiftUIDemo; + packageProductDependencies = ( + 32D5D1752A445C8F0098BDFC /* SDWebImageSwiftUI */, + 32B13E882AA368CC00BE9B5B /* SDWebImage */, + ); productName = SDWebImageSwiftUIDemo; productReference = 320CDC2922FADB44007CF858 /* SDWebImageSwiftUIDemo.app */; productType = "com.apple.product-type.application"; @@ -488,11 +482,9 @@ isa = PBXNativeTarget; buildConfigurationList = 322E0DEE28D3318B0003A55F /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests" */; buildPhases = ( - C7AF6D10A677FCEBE3437F0D /* [CP] Check Pods Manifest.lock */, 322E0DE228D3318B0003A55F /* Sources */, 322E0DE328D3318B0003A55F /* Frameworks */, 322E0DE428D3318B0003A55F /* Resources */, - FBC7E7B3AE428B3A9E53818E /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -502,7 +494,6 @@ ); name = SDWebImageSwiftUITests; packageProductDependencies = ( - 32DCFE9428D333E8001A17BF /* ViewInspector */, ); productName = SDWebImageSwiftUITests; productReference = 322E0DE628D3318B0003A55F /* SDWebImageSwiftUITests.xctest */; @@ -512,11 +503,9 @@ isa = PBXNativeTarget; buildConfigurationList = 322E0E0828D331F00003A55F /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests macOS" */; buildPhases = ( - C82E8A3FDE233B48BF0E7FD0 /* [CP] Check Pods Manifest.lock */, 322E0DFE28D331F00003A55F /* Sources */, 322E0DFF28D331F00003A55F /* Frameworks */, 322E0E0028D331F00003A55F /* Resources */, - 8D8B832471DE6E5EE5ABE934 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -526,7 +515,6 @@ ); name = "SDWebImageSwiftUITests macOS"; packageProductDependencies = ( - 327B90F128DC4EBB003E8BD9 /* ViewInspector */, ); productName = "SDWebImageSwiftUITests macOS"; productReference = 322E0E0228D331F00003A55F /* SDWebImageSwiftUITests macOS.xctest */; @@ -536,11 +524,9 @@ isa = PBXNativeTarget; buildConfigurationList = 322E0E1528D332050003A55F /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests tvOS" */; buildPhases = ( - 8C3AFE728247BB6B9A847044 /* [CP] Check Pods Manifest.lock */, 322E0E0B28D332050003A55F /* Sources */, 322E0E0C28D332050003A55F /* Frameworks */, 322E0E0D28D332050003A55F /* Resources */, - 5EA97551EBAEA1D25997F2AB /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -550,27 +536,49 @@ ); name = "SDWebImageSwiftUITests tvOS"; packageProductDependencies = ( - 327B90F328DC4EC0003E8BD9 /* ViewInspector */, ); productName = "SDWebImageSwiftUITests tvOS"; productReference = 322E0E0F28D332050003A55F /* SDWebImageSwiftUITests tvOS.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 32D5D15F2A445B250098BDFC /* SDWebImageSwiftUIDemo-visionOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 32D5D1712A445B260098BDFC /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo-visionOS" */; + buildPhases = ( + 32D5D15C2A445B250098BDFC /* Sources */, + 32D5D15D2A445B250098BDFC /* Frameworks */, + 32D5D15E2A445B250098BDFC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "SDWebImageSwiftUIDemo-visionOS"; + packageProductDependencies = ( + 32B13E802AA368B700BE9B5B /* SDWebImageSwiftUI */, + 32B13E822AA368B900BE9B5B /* SDWebImage */, + ); + productName = "SDWebImageSwiftUIDemo-visionOS"; + productReference = 32D5D1602A445B250098BDFC /* SDWebImageSwiftUIDemo-visionOS.app */; + productType = "com.apple.product-type.application"; + }; 32E529082348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS */ = { isa = PBXNativeTarget; buildConfigurationList = 32E5291B2348A0C900EA46FF /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo-macOS" */; buildPhases = ( - 78426DFA5212E5496802AC58 /* [CP] Check Pods Manifest.lock */, 32E529052348A0C700EA46FF /* Sources */, 32E529062348A0C700EA46FF /* Frameworks */, 32E529072348A0C700EA46FF /* Resources */, - 4577A2F4A5DEBBDBB766F1CF /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = "SDWebImageSwiftUIDemo-macOS"; + packageProductDependencies = ( + 32B13E842AA368C600BE9B5B /* SDWebImageSwiftUI */, + 32B13E862AA368C900BE9B5B /* SDWebImage */, + ); productName = "SDWebImageSwiftUIDemo-macOS"; productReference = 32E529092348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS.app */; productType = "com.apple.product-type.application"; @@ -579,17 +587,19 @@ isa = PBXNativeTarget; buildConfigurationList = 32E5292F2348A0D400EA46FF /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo-tvOS" */; buildPhases = ( - FB46C9F77AA45C7DA1D71F7B /* [CP] Check Pods Manifest.lock */, 32E5291C2348A0D300EA46FF /* Sources */, 32E5291D2348A0D300EA46FF /* Frameworks */, 32E5291E2348A0D300EA46FF /* Resources */, - A6F5B1BDEE1460B7F20E55C6 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = "SDWebImageSwiftUIDemo-tvOS"; + packageProductDependencies = ( + 32B13E8E2AA368E100BE9B5B /* SDWebImageSwiftUI */, + 32B13E902AA368E300BE9B5B /* SDWebImage */, + ); productName = "SDWebImageSwiftUIDemo-tvOS"; productReference = 32E529202348A0D300EA46FF /* SDWebImageSwiftUIDemo-tvOS.app */; productType = "com.apple.product-type.application"; @@ -626,6 +636,10 @@ 32E529492348A0DE00EA46FF /* PBXTargetDependency */, ); name = "SDWebImageSwiftUIDemo-watchOS WatchKit App"; + packageProductDependencies = ( + 32B13E922AA368EF00BE9B5B /* SDWebImageSwiftUI */, + 32B13E942AA368F300BE9B5B /* SDWebImage */, + ); productName = "SDWebImageSwiftUIDemo-watchOS WatchKit App"; productReference = 32E529372348A0DD00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit App.app */; productType = "com.apple.product-type.application.watchapp2"; @@ -634,11 +648,9 @@ isa = PBXNativeTarget; buildConfigurationList = 32E529572348A0DF00EA46FF /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo-watchOS WatchKit Extension" */; buildPhases = ( - B9B631F1DB90E98FCAE3E196 /* [CP] Check Pods Manifest.lock */, 32E529422348A0DE00EA46FF /* Sources */, 32E529432348A0DE00EA46FF /* Frameworks */, 32E529442348A0DE00EA46FF /* Resources */, - 756F0513F095D448FCCD78A2 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -655,7 +667,7 @@ 607FACC81AFB9204008FA782 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1340; + LastSwiftUpdateCheck = 1500; LastUpgradeCheck = 0830; ORGANIZATIONNAME = CocoaPods; TargetAttributes = { @@ -677,6 +689,9 @@ ProvisioningStyle = Automatic; TestTargetID = 32E5291F2348A0D300EA46FF; }; + 32D5D15F2A445B250098BDFC = { + CreatedOnToolsVersion = 15.0; + }; 32E529082348A0C700EA46FF = { CreatedOnToolsVersion = 11.0; ProvisioningStyle = Automatic; @@ -710,7 +725,7 @@ ); mainGroup = 607FACC71AFB9204008FA782; packageReferences = ( - 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */, + 3294617A2AA36734009E391B /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */, ); productRefGroup = 607FACD11AFB9204008FA782 /* Products */; projectDirPath = ""; @@ -722,6 +737,7 @@ 32E529332348A0DD00EA46FF /* SDWebImageSwiftUIDemo-watchOS */, 32E529362348A0DD00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit App */, 32E529452348A0DE00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit Extension */, + 32D5D15F2A445B250098BDFC /* SDWebImageSwiftUIDemo-visionOS */, 322E0DE528D3318B0003A55F /* SDWebImageSwiftUITests */, 322E0E0128D331F00003A55F /* SDWebImageSwiftUITests macOS */, 322E0E0E28D332050003A55F /* SDWebImageSwiftUITests tvOS */, @@ -764,6 +780,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 32D5D15E2A445B250098BDFC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 32D5D16E2A445B260098BDFC /* Preview Assets.xcassets in Resources */, + 32D5D16B2A445B260098BDFC /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 32E529072348A0C700EA46FF /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -811,335 +836,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 0B5ABDA8213E875CE5FCC890 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo/Pods-SDWebImageSwiftUIDemo-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/SDWebImage-iOS/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImagePDFCoder-iOS/SDWebImagePDFCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSVGCoder-iOS/SDWebImageSVGCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSwiftUI-iOS/SDWebImageSwiftUI.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder-iOS/SDWebImageWebPCoder.framework", - "${BUILT_PRODUCTS_DIR}/libwebp-iOS/libwebp.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImagePDFCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSVGCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSwiftUI.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo/Pods-SDWebImageSwiftUIDemo-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 4577A2F4A5DEBBDBB766F1CF /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo-macOS/Pods-SDWebImageSwiftUIDemo-macOS-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/SDWebImage-macOS/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImagePDFCoder-macOS/SDWebImagePDFCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSVGCoder-macOS/SDWebImageSVGCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSwiftUI-macOS/SDWebImageSwiftUI.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder-macOS/SDWebImageWebPCoder.framework", - "${BUILT_PRODUCTS_DIR}/libwebp-macOS/libwebp.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImagePDFCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSVGCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSwiftUI.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo-macOS/Pods-SDWebImageSwiftUIDemo-macOS-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 58D483FABAB44B4EC2E538D0 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SDWebImageSwiftUIDemo-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 5EA97551EBAEA1D25997F2AB /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUITests tvOS/Pods-SDWebImageSwiftUITests tvOS-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/SDWebImage-tvOS/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSwiftUI-tvOS/SDWebImageSwiftUI.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSwiftUI.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUITests tvOS/Pods-SDWebImageSwiftUITests tvOS-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 756F0513F095D448FCCD78A2 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/SDWebImage-watchOS/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImagePDFCoder-watchOS/SDWebImagePDFCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSVGCoder-watchOS/SDWebImageSVGCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSwiftUI-watchOS/SDWebImageSwiftUI.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder-watchOS/SDWebImageWebPCoder.framework", - "${BUILT_PRODUCTS_DIR}/libwebp-watchOS/libwebp.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImagePDFCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSVGCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSwiftUI.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 78426DFA5212E5496802AC58 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SDWebImageSwiftUIDemo-macOS-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 8C3AFE728247BB6B9A847044 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SDWebImageSwiftUITests tvOS-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 8D8B832471DE6E5EE5ABE934 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUITests macOS/Pods-SDWebImageSwiftUITests macOS-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/SDWebImage-macOS/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSwiftUI-macOS/SDWebImageSwiftUI.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSwiftUI.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUITests macOS/Pods-SDWebImageSwiftUITests macOS-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - A6F5B1BDEE1460B7F20E55C6 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo-tvOS/Pods-SDWebImageSwiftUIDemo-tvOS-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/SDWebImage-tvOS/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImagePDFCoder-tvOS/SDWebImagePDFCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSVGCoder-tvOS/SDWebImageSVGCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSwiftUI-tvOS/SDWebImageSwiftUI.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder-tvOS/SDWebImageWebPCoder.framework", - "${BUILT_PRODUCTS_DIR}/libwebp-tvOS/libwebp.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImagePDFCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSVGCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSwiftUI.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo-tvOS/Pods-SDWebImageSwiftUIDemo-tvOS-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - B9B631F1DB90E98FCAE3E196 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - C7AF6D10A677FCEBE3437F0D /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SDWebImageSwiftUITests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - C82E8A3FDE233B48BF0E7FD0 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SDWebImageSwiftUITests macOS-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - FB46C9F77AA45C7DA1D71F7B /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SDWebImageSwiftUIDemo-tvOS-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - FBC7E7B3AE428B3A9E53818E /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUITests/Pods-SDWebImageSwiftUITests-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/SDWebImage-iOS/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSwiftUI-iOS/SDWebImageSwiftUI.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSwiftUI.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUITests/Pods-SDWebImageSwiftUITests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 320CDC2522FADB44007CF858 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -1188,6 +884,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 32D5D15C2A445B250098BDFC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 32D5D1672A445B260098BDFC /* AppDelegate.swift in Sources */, + 32D5D1722A445BF00098BDFC /* DetailView.swift in Sources */, + 32D5D1732A445BF00098BDFC /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 32E529052348A0C700EA46FF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1299,7 +1005,6 @@ /* Begin XCBuildConfiguration section */ 320CDC3A22FADB45007CF858 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A78BA7FB5AFF53CBDD4C4CBD /* Pods-SDWebImageSwiftUIDemo.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -1337,7 +1042,6 @@ }; 320CDC3B22FADB45007CF858 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5864FFEDE62A0630EDF26A56 /* Pods-SDWebImageSwiftUIDemo.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -1372,7 +1076,6 @@ }; 322E0DEC28D3318B0003A55F /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 487B2863C76EC4CE36CEC4EA /* Pods-SDWebImageSwiftUITests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1404,7 +1107,6 @@ }; 322E0DED28D3318B0003A55F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = DDE527DE0EF6B6D9B7CD8C97 /* Pods-SDWebImageSwiftUITests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1433,7 +1135,6 @@ }; 322E0E0928D331F00003A55F /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A7CD2F7825F1936052B2C65E /* Pods-SDWebImageSwiftUITests macOS.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1465,7 +1166,6 @@ }; 322E0E0A28D331F00003A55F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0EEE88A04A9B191BD966EFC2 /* Pods-SDWebImageSwiftUITests macOS.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1494,7 +1194,6 @@ }; 322E0E1628D332050003A55F /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5ABE9344AF344CCC70056CD5 /* Pods-SDWebImageSwiftUITests tvOS.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1527,7 +1226,6 @@ }; 322E0E1728D332050003A55F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B6E12746E84E9ED7FA910A24 /* Pods-SDWebImageSwiftUITests tvOS.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1555,9 +1253,95 @@ }; name = Release; }; + 32D5D16F2A445B260098BDFC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_ASSET_PATHS = "\"SDWebImageSwiftUIDemo-visionOS/Preview Content\""; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "SDWebImageSwiftUIDemo-visionOS/Info.plist"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.SDWebImageSwiftUIDemo-visionOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = xros; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 1.0; + }; + name = Debug; + }; + 32D5D1702A445B260098BDFC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"SDWebImageSwiftUIDemo-visionOS/Preview Content\""; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "SDWebImageSwiftUIDemo-visionOS/Info.plist"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.SDWebImageSwiftUIDemo-visionOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = xros; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 1.0; + }; + name = Release; + }; 32E529192348A0C900EA46FF /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 89B11BBDBAA86F760DF1EE2D /* Pods-SDWebImageSwiftUIDemo-macOS.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -1593,7 +1377,6 @@ }; 32E5291A2348A0C900EA46FF /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F3AACDC116F5598BC39A8573 /* Pods-SDWebImageSwiftUIDemo-macOS.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -1626,7 +1409,6 @@ }; 32E529302348A0D400EA46FF /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 746AF60263F54FD7E16AA7D5 /* Pods-SDWebImageSwiftUIDemo-tvOS.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; CLANG_ANALYZER_NONNULL = YES; @@ -1661,7 +1443,6 @@ }; 32E529312348A0D400EA46FF /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 95C9E0D9CE4113E5A82480B9 /* Pods-SDWebImageSwiftUIDemo-tvOS.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; CLANG_ANALYZER_NONNULL = YES; @@ -1693,7 +1474,6 @@ }; 32E529582348A0DF00EA46FF /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FEED4964309E241D2FD8A544 /* Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; CLANG_ANALYZER_NONNULL = YES; @@ -1730,7 +1510,6 @@ }; 32E529592348A0DF00EA46FF /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7B0D9182CAD02B73E6F208F3 /* Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; CLANG_ANALYZER_NONNULL = YES; @@ -2007,6 +1786,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 32D5D1712A445B260098BDFC /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo-visionOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 32D5D16F2A445B260098BDFC /* Debug */, + 32D5D1702A445B260098BDFC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 32E5291B2348A0C900EA46FF /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo-macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -2064,6 +1852,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 3294617A2AA36734009E391B /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SDWebImage/SDWebImageSwiftUI.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.2.3; + }; + }; 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/nalexn/ViewInspector.git"; @@ -2085,20 +1881,45 @@ package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; productName = ViewInspector; }; - 327B90F128DC4EBB003E8BD9 /* ViewInspector */ = { + 32B13E802AA368B700BE9B5B /* SDWebImageSwiftUI */ = { isa = XCSwiftPackageProductDependency; - package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; - productName = ViewInspector; + productName = SDWebImageSwiftUI; }; - 327B90F328DC4EC0003E8BD9 /* ViewInspector */ = { + 32B13E822AA368B900BE9B5B /* SDWebImage */ = { isa = XCSwiftPackageProductDependency; - package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; - productName = ViewInspector; + productName = SDWebImage; }; - 32DCFE9428D333E8001A17BF /* ViewInspector */ = { + 32B13E842AA368C600BE9B5B /* SDWebImageSwiftUI */ = { isa = XCSwiftPackageProductDependency; - package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; - productName = ViewInspector; + productName = SDWebImageSwiftUI; + }; + 32B13E862AA368C900BE9B5B /* SDWebImage */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImage; + }; + 32B13E882AA368CC00BE9B5B /* SDWebImage */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImage; + }; + 32B13E8E2AA368E100BE9B5B /* SDWebImageSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImageSwiftUI; + }; + 32B13E902AA368E300BE9B5B /* SDWebImage */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImage; + }; + 32B13E922AA368EF00BE9B5B /* SDWebImageSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImageSwiftUI; + }; + 32B13E942AA368F300BE9B5B /* SDWebImage */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImage; + }; + 32D5D1752A445C8F0098BDFC /* SDWebImageSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImageSwiftUI; }; 32DCFE9628D333F1001A17BF /* ViewInspector */ = { isa = XCSwiftPackageProductDependency; diff --git a/Example/SDWebImageSwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/SDWebImageSwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 8c78f63a..919434a6 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/Example/SDWebImageSwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-visionOS.xcscheme b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-visionOS.xcscheme new file mode 100644 index 00000000..96453419 --- /dev/null +++ b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-visionOS.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme index 3d1f081f..65df20bc 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme +++ b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme @@ -54,10 +54,8 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> - + - + - + - - - - - + diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/AppDelegate.swift b/Example/SDWebImageSwiftUIDemo-visionOS/AppDelegate.swift new file mode 100644 index 00000000..0939fca7 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/AppDelegate.swift @@ -0,0 +1,41 @@ +/* + * This file is part of the SDWebImage package. + * (c) DreamPiggy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import SwiftUI +import UIKit +import SDWebImage + +// no changes in your AppDelegate class +class AppDelegate: NSObject, UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { + // Add WebP/SVG/PDF support + SDImageCodersManager.shared.addCoder(SDImageAWebPCoder.shared) + // Dynamic check to support vector format for both WebImage/AnimatedImage + SDWebImageManager.shared.optionsProcessor = SDWebImageOptionsProcessor { url, options, context in + var options = options + if let _ = context?[.animatedImageClass] as? SDAnimatedImage.Type { + // AnimatedImage supports vector rendering, should not force decode + options.insert(.avoidDecodeImage) + } + return SDWebImageOptionsResult(options: options, context: context) + } + return true + } +} + +@main +struct SDWebImageSwiftUIDemo: App { + // inject into SwiftUI life-cycle via adaptor + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000..0c7eecb9 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "reality", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Contents.json new file mode 100644 index 00000000..950af4d8 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.solidimagestacklayer" + }, + { + "filename" : "Middle.solidimagestacklayer" + }, + { + "filename" : "Back.solidimagestacklayer" + } + ] +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000..0c7eecb9 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "reality", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000..0c7eecb9 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "reality", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Info.plist b/Example/SDWebImageSwiftUIDemo-visionOS/Info.plist new file mode 100644 index 00000000..6a6654d9 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Info.plist @@ -0,0 +1,11 @@ + + + + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + + diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Preview Content/Preview Assets.xcassets/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index a3619c90..1fd39279 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -112,7 +112,7 @@ struct ContentView: View { NavigationLink(destination: DetailView(url: url, animated: self.animated)) { HStack { if self.animated { - #if os(macOS) || os(iOS) || os(tvOS) + #if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) AnimatedImage(url: URL(string:url), isAnimating: .constant(true)) .onViewUpdate { view, context in #if os(macOS) @@ -157,6 +157,20 @@ struct ContentView: View { var body: some View { + #if os(visionOS) + return NavigationView { + contentView() + .navigationBarTitle(animated ? "AnimatedImage" : "WebImage") + .navigationBarItems(leading: + Button(action: { self.reloadCache() }) { + Text("Reload") + }, trailing: + Button(action: { self.switchView() }) { + Text("Switch") + } + ) + } + #endif #if os(iOS) return NavigationView { contentView() diff --git a/Example/SDWebImageSwiftUIDemo/DetailView.swift b/Example/SDWebImageSwiftUIDemo/DetailView.swift index f9e7dea0..74032170 100644 --- a/Example/SDWebImageSwiftUIDemo/DetailView.swift +++ b/Example/SDWebImageSwiftUIDemo/DetailView.swift @@ -44,7 +44,7 @@ struct DetailView: View { var body: some View { VStack { - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) zoomView() .navigationBarItems(trailing: Button(isAnimating ? "Stop" : "Start") { self.isAnimating.toggle() @@ -62,7 +62,7 @@ struct DetailView: View { } func zoomView() -> some View { - #if os(macOS) || os(iOS) + #if os(macOS) || os(iOS) || os(visionOS) return contentView() .scaleEffect(self.scale) .gesture(MagnificationGesture(minimumScaleDelta: 0.1).onChanged { value in @@ -94,7 +94,7 @@ struct DetailView: View { func contentView() -> some View { HStack { if animated { - #if os(macOS) || os(iOS) || os(tvOS) + #if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) AnimatedImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating) .resizable() .placeholder(.wifiExclamationmark) diff --git a/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/SDWebImageSwiftUI.xcodeproj/project.pbxproj index 1fbde810..dcbdb696 100644 --- a/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -403,8 +403,6 @@ Base, ); mainGroup = 32C43DC222FD540D00BE87F5; - packageReferences = ( - ); productRefGroup = 32C43DCD22FD540D00BE87F5 /* Products */; projectDirPath = ""; projectRoot = ""; diff --git a/SDWebImageSwiftUI.xcworkspace/contents.xcworkspacedata b/SDWebImageSwiftUI.xcworkspace/contents.xcworkspacedata index 496425a8..9b0b7f6b 100644 --- a/SDWebImageSwiftUI.xcworkspace/contents.xcworkspacedata +++ b/SDWebImageSwiftUI.xcworkspace/contents.xcworkspacedata @@ -4,7 +4,4 @@ - - diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index e454432f..59a50a28 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -9,7 +9,7 @@ import SwiftUI import SDWebImage -#if os(iOS) || os(tvOS) || os(macOS) +#if !os(watchOS) /// A coordinator object used for `AnimatedImage`native view bridge for UIKit/AppKit. @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) @@ -183,7 +183,7 @@ public struct AnimatedImage : PlatformViewRepresentable { #if os(macOS) public typealias NSViewType = AnimatedImageViewWrapper - #elseif os(iOS) || os(tvOS) + #else public typealias UIViewType = AnimatedImageViewWrapper #endif @@ -205,7 +205,7 @@ public struct AnimatedImage : PlatformViewRepresentable { public static func dismantleNSView(_ nsView: AnimatedImageViewWrapper, coordinator: Coordinator) { dismantleView(nsView, coordinator: coordinator) } - #elseif os(iOS) || os(tvOS) + #else public func makeUIView(context: Context) -> AnimatedImageViewWrapper { makeView(context: context) } @@ -373,14 +373,14 @@ public struct AnimatedImage : PlatformViewRepresentable { // AspectRatio && ContentMode #if os(macOS) let contentMode: NSImageScaling - #elseif os(iOS) || os(tvOS) + #else let contentMode: UIView.ContentMode #endif if let _ = imageLayout.aspectRatio { // If `aspectRatio` is not `nil`, always scale to fill and SwiftUI will layout the container with custom aspect ratio. #if os(macOS) contentMode = .scaleAxesIndependently - #elseif os(iOS) || os(tvOS) + #else contentMode = .scaleToFill #endif } else { @@ -391,20 +391,20 @@ public struct AnimatedImage : PlatformViewRepresentable { // Actually, NSImageView have no `.aspectFill` unlike UIImageView, only `CALayerContentsGravity.resizeAspectFill` have the same concept // However, using `.scaleProportionallyUpOrDown`, SwiftUI still layout the HostingView correctly, so this is OK contentMode = .scaleProportionallyUpOrDown - #elseif os(iOS) || os(tvOS) + #else contentMode = .scaleAspectFill #endif case .fit: #if os(macOS) contentMode = .scaleProportionallyUpOrDown - #elseif os(iOS) || os(tvOS) + #else contentMode = .scaleAspectFit #endif case .none: // If `contentMode` is not set at all, using scale to fill as SwiftUI default value #if os(macOS) contentMode = .scaleAxesIndependently - #elseif os(iOS) || os(tvOS) + #else contentMode = .scaleToFill #endif } diff --git a/SDWebImageSwiftUI/Classes/Image.swift b/SDWebImageSwiftUI/Classes/Image.swift index 28ba9697..c4b46b86 100644 --- a/SDWebImageSwiftUI/Classes/Image.swift +++ b/SDWebImageSwiftUI/Classes/Image.swift @@ -25,7 +25,7 @@ extension PlatformImage { static var empty = PlatformImage() } -#if os(iOS) || os(tvOS) || os(watchOS) +#if !os(macOS) @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) extension PlatformImage.Orientation { @inlinable var toSwiftUI: Image.Orientation { diff --git a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift index 80f936ee..51de9ca7 100644 --- a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift +++ b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift @@ -9,7 +9,7 @@ import Foundation import SDWebImage -#if os(iOS) || os(tvOS) || os(macOS) +#if !os(watchOS) /// Use wrapper to solve tne `UIImageView`/`NSImageView` frame size become image size issue (SwiftUI's Bug) @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) diff --git a/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift b/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift index 7103c77e..0b737b3d 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift @@ -8,7 +8,7 @@ import SwiftUI -#if os(macOS) || os(iOS) || os(tvOS) +#if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) /// An activity indicator (system style) @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) public struct ActivityIndicator: PlatformViewRepresentable { @@ -26,11 +26,11 @@ public struct ActivityIndicator: PlatformViewRepresentable { #if os(macOS) public typealias NSViewType = NSProgressIndicator - #elseif os(iOS) || os(tvOS) + #else public typealias UIViewType = UIActivityIndicatorView #endif - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) public func makeUIView(context: UIViewRepresentableContext) -> UIActivityIndicatorView { let activityStyle: UIActivityIndicatorView.Style switch style { diff --git a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift index 37eb9030..925fd6a2 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift @@ -58,7 +58,7 @@ public struct IndicatorViewModifier : ViewModifier where T : View { } } -#if os(macOS) || os(iOS) || os(tvOS) +#if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) extension Indicator where T == ActivityIndicator { /// Activity Indicator diff --git a/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift b/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift index 1256ee87..6f1acba4 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift @@ -8,7 +8,7 @@ import SwiftUI -#if os(macOS) || os(iOS) || os(tvOS) +#if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) /// A progress bar indicator (system style) @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) public struct ProgressIndicator: PlatformViewRepresentable { @@ -29,11 +29,11 @@ public struct ProgressIndicator: PlatformViewRepresentable { #if os(macOS) public typealias NSViewType = ProgressIndicatorWrapper - #elseif os(iOS) || os(tvOS) + #else public typealias UIViewType = ProgressIndicatorWrapper #endif - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) public func makeUIView(context: UIViewRepresentableContext) -> ProgressIndicatorWrapper { let progressStyle: UIProgressView.Style switch style { diff --git a/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift b/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift index 9c0e71ad..3e06e6ee 100644 --- a/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift +++ b/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift @@ -22,7 +22,7 @@ public typealias PlatformImage = UIImage @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) public typealias PlatformView = NSView #endif -#if os(iOS) || os(tvOS) +#if os(iOS) || os(tvOS) || os(visionOS) @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) public typealias PlatformView = UIView #endif @@ -35,7 +35,7 @@ public typealias PlatformView = WKInterfaceObject @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) public typealias PlatformViewRepresentable = NSViewRepresentable #endif -#if os(iOS) || os(tvOS) +#if os(iOS) || os(tvOS) || os(visionOS) @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) public typealias PlatformViewRepresentable = UIViewRepresentable #endif @@ -50,7 +50,7 @@ extension NSViewRepresentable { typealias PlatformViewType = NSViewType } #endif -#if os(iOS) || os(tvOS) +#if os(iOS) || os(tvOS) || os(visionOS) @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) extension UIViewRepresentable { typealias PlatformViewType = UIViewType diff --git a/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift b/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift index e7a46393..b285b7d5 100644 --- a/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift +++ b/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift @@ -9,14 +9,14 @@ import Foundation import SwiftUI -#if os(iOS) || os(tvOS) || os(macOS) +#if !os(watchOS) @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) struct PlatformAppear: PlatformViewRepresentable { let appearAction: () -> Void let disappearAction: () -> Void - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) func makeUIView(context: Context) -> some UIView { let view = PlatformAppearView() view.appearAction = appearAction diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 65fdf15e..940edf99 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -64,10 +64,10 @@ public struct WebImage : View { @ObservedObject var indicatorStatus : IndicatorStatus // FIXME: Use SwiftUI StateObject and remove onPlatformAppear once drop iOS 13 support - @Backport.StateObject var imagePlayer = ImagePlayer() + @StateObject var imagePlayer = ImagePlayer() // FIXME: Use SwiftUI StateObject and remove onPlatformAppear once drop iOS 13 support - @Backport.StateObject var imageManager : ImageManager + @StateObject var imageManager : ImageManager /// Create a web image with url, placeholder, custom options and context. Optional can support animated image using Binding. /// - Parameter url: The image url @@ -89,7 +89,7 @@ public struct WebImage : View { imageModel.context = context _imageModel = ObservedObject(wrappedValue: imageModel) let imageManager = ImageManager() - _imageManager = Backport.StateObject(wrappedValue: imageManager) + _imageManager = StateObject(wrappedValue: imageManager) _indicatorStatus = ObservedObject(wrappedValue: imageManager.indicatorStatus) } @@ -160,9 +160,11 @@ public struct WebImage : View { // ensure CGImage is nil if image.cgImage == nil { // draw vector into bitmap with the screen scale (behavior like AppKit) - #if os(iOS) || os(tvOS) + #if os(visionOS) + let scale = UITraitCollection.current.displayScale + #elseif os(iOS) || os(tvOS) || os(visionOS) let scale = UIScreen.main.scale - #else + #elseif os(watchOS) let scale = WKInterfaceDevice.current().screenScale #endif UIGraphicsBeginImageContextWithOptions(image.size, false, scale) From 2c24b7f2b7029da5a4d97cf9f74c6f18bd5cd504 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 2 Sep 2023 21:28:07 +0800 Subject: [PATCH 27/90] Added readme about visionOS --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index d64f3c3f..ba983fda 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,14 @@ It brings all your favorite features from SDWebImage, like async image loading, The framework provide the different View structs, which API match the SwiftUI framework guideline. If you're familiar with `Image`, you'll find it easy to use `WebImage` and `AnimatedImage`. +## Apple VisionOS + +SDWebImageSwiftUI can compiled for visionOS platform. However, due to the package manager support, we can not integrate using the exists function. + +So, in [visionOS branch](https://github.com/SDWebImage/SDWebImageSwiftUI/tree/feature/visionOS), our demo use the Xcode's built-in dependency to build. + +You can build and run to see the example (still need improvement) + ## Features Since SDWebImageSwiftUI is built on top of SDWebImage, it provide both the out-of-box features as well as advanced powerful features you may want in real world Apps. Check our [Wiki](https://github.com/SDWebImage/SDWebImage/wiki/Advanced-Usage) when you need: From 8b7ab6f3af387269ee7e5b746386c7b2f42e1934 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 2 Sep 2023 21:56:56 +0800 Subject: [PATCH 28/90] Update the Demo with SPM instead of CocoaPods --- .../project.pbxproj | 179 ++++++++++++++++-- .../AppDelegate.swift | 7 +- README.md | 8 +- 3 files changed, 175 insertions(+), 19 deletions(-) diff --git a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj index e7e62e29..7083664c 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ @@ -29,6 +29,23 @@ 322E0E2228D332130003A55F /* Images.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 322E0DF228D331A20003A55F /* Images.bundle */; }; 322E0E2328D332130003A55F /* Images.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 322E0DF228D331A20003A55F /* Images.bundle */; }; 326B0D712345C01900D28269 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; + 32ABE4D92AA3753300331406 /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4D82AA3753300331406 /* SDWebImageWebPCoder */; }; + 32ABE4DC2AA3755D00331406 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4DB2AA3755D00331406 /* SDWebImageSVGCoder */; }; + 32ABE4DF2AA3756A00331406 /* SDWebImagePDFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4DE2AA3756A00331406 /* SDWebImagePDFCoder */; }; + 32ABE4E12AA3757B00331406 /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4E02AA3757B00331406 /* SDWebImageWebPCoder */; }; + 32ABE4E32AA3757B00331406 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4E22AA3757B00331406 /* SDWebImageSVGCoder */; }; + 32ABE4E52AA3757B00331406 /* SDWebImagePDFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4E42AA3757B00331406 /* SDWebImagePDFCoder */; }; + 32ABE4E72AA3758400331406 /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4E62AA3758400331406 /* SDWebImageWebPCoder */; }; + 32ABE4E92AA3758400331406 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4E82AA3758400331406 /* SDWebImageSVGCoder */; }; + 32ABE4EB2AA3758400331406 /* SDWebImagePDFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4EA2AA3758400331406 /* SDWebImagePDFCoder */; }; + 32ABE4F32AA3759900331406 /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4F22AA3759900331406 /* SDWebImageWebPCoder */; }; + 32ABE4F52AA3759900331406 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4F42AA3759900331406 /* SDWebImageSVGCoder */; }; + 32ABE4F72AA3759900331406 /* SDWebImagePDFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4F62AA3759900331406 /* SDWebImagePDFCoder */; }; + 32ABE4F92AA375A500331406 /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4F82AA375A500331406 /* SDWebImage */; }; + 32ABE4FD2AA375A500331406 /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4FC2AA375A500331406 /* SDWebImageWebPCoder */; }; + 32ABE4FF2AA375A500331406 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4FE2AA375A500331406 /* SDWebImageSVGCoder */; }; + 32ABE5012AA375A500331406 /* SDWebImagePDFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE5002AA375A500331406 /* SDWebImagePDFCoder */; }; + 32ABE5032AA375B400331406 /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE5022AA375B400331406 /* SDWebImageSwiftUI */; }; 32B13E812AA368B700BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E802AA368B700BE9B5B /* SDWebImageSwiftUI */; }; 32B13E832AA368B900BE9B5B /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E822AA368B900BE9B5B /* SDWebImage */; }; 32B13E852AA368C600BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E842AA368C600BE9B5B /* SDWebImageSwiftUI */; }; @@ -36,8 +53,6 @@ 32B13E892AA368CC00BE9B5B /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E882AA368CC00BE9B5B /* SDWebImage */; }; 32B13E8F2AA368E100BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E8E2AA368E100BE9B5B /* SDWebImageSwiftUI */; }; 32B13E912AA368E300BE9B5B /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E902AA368E300BE9B5B /* SDWebImage */; }; - 32B13E932AA368EF00BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E922AA368EF00BE9B5B /* SDWebImageSwiftUI */; }; - 32B13E952AA368F300BE9B5B /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E942AA368F300BE9B5B /* SDWebImage */; }; 32D5D1672A445B260098BDFC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5D1662A445B260098BDFC /* AppDelegate.swift */; }; 32D5D16B2A445B260098BDFC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32D5D16A2A445B260098BDFC /* Assets.xcassets */; }; 32D5D16E2A445B260098BDFC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32D5D16D2A445B260098BDFC /* Preview Assets.xcassets */; }; @@ -193,6 +208,9 @@ files = ( 32B13E892AA368CC00BE9B5B /* SDWebImage in Frameworks */, 32D5D1762A445C8F0098BDFC /* SDWebImageSwiftUI in Frameworks */, + 32ABE4D92AA3753300331406 /* SDWebImageWebPCoder in Frameworks */, + 32ABE4DF2AA3756A00331406 /* SDWebImagePDFCoder in Frameworks */, + 32ABE4DC2AA3755D00331406 /* SDWebImageSVGCoder in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -223,6 +241,9 @@ files = ( 32B13E832AA368B900BE9B5B /* SDWebImage in Frameworks */, 32B13E812AA368B700BE9B5B /* SDWebImageSwiftUI in Frameworks */, + 32ABE4F32AA3759900331406 /* SDWebImageWebPCoder in Frameworks */, + 32ABE4F72AA3759900331406 /* SDWebImagePDFCoder in Frameworks */, + 32ABE4F52AA3759900331406 /* SDWebImageSVGCoder in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -232,6 +253,9 @@ files = ( 32B13E872AA368C900BE9B5B /* SDWebImage in Frameworks */, 32B13E852AA368C600BE9B5B /* SDWebImageSwiftUI in Frameworks */, + 32ABE4E12AA3757B00331406 /* SDWebImageWebPCoder in Frameworks */, + 32ABE4E52AA3757B00331406 /* SDWebImagePDFCoder in Frameworks */, + 32ABE4E32AA3757B00331406 /* SDWebImageSVGCoder in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -241,6 +265,9 @@ files = ( 32B13E912AA368E300BE9B5B /* SDWebImage in Frameworks */, 32B13E8F2AA368E100BE9B5B /* SDWebImageSwiftUI in Frameworks */, + 32ABE4E72AA3758400331406 /* SDWebImageWebPCoder in Frameworks */, + 32ABE4EB2AA3758400331406 /* SDWebImagePDFCoder in Frameworks */, + 32ABE4E92AA3758400331406 /* SDWebImageSVGCoder in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -248,6 +275,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 32ABE5032AA375B400331406 /* SDWebImageSwiftUI in Frameworks */, + 32ABE4F92AA375A500331406 /* SDWebImage in Frameworks */, + 32ABE4FD2AA375A500331406 /* SDWebImageWebPCoder in Frameworks */, + 32ABE5012AA375A500331406 /* SDWebImagePDFCoder in Frameworks */, + 32ABE4FF2AA375A500331406 /* SDWebImageSVGCoder in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -262,8 +294,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 32B13E952AA368F300BE9B5B /* SDWebImage in Frameworks */, - 32B13E932AA368EF00BE9B5B /* SDWebImageSwiftUI in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -468,6 +498,9 @@ packageProductDependencies = ( 32D5D1752A445C8F0098BDFC /* SDWebImageSwiftUI */, 32B13E882AA368CC00BE9B5B /* SDWebImage */, + 32ABE4D82AA3753300331406 /* SDWebImageWebPCoder */, + 32ABE4DB2AA3755D00331406 /* SDWebImageSVGCoder */, + 32ABE4DE2AA3756A00331406 /* SDWebImagePDFCoder */, ); productName = SDWebImageSwiftUIDemo; productReference = 320CDC2922FADB44007CF858 /* SDWebImageSwiftUIDemo.app */; @@ -552,6 +585,9 @@ packageProductDependencies = ( 32B13E802AA368B700BE9B5B /* SDWebImageSwiftUI */, 32B13E822AA368B900BE9B5B /* SDWebImage */, + 32ABE4F22AA3759900331406 /* SDWebImageWebPCoder */, + 32ABE4F42AA3759900331406 /* SDWebImageSVGCoder */, + 32ABE4F62AA3759900331406 /* SDWebImagePDFCoder */, ); productName = "SDWebImageSwiftUIDemo-visionOS"; productReference = 32D5D1602A445B250098BDFC /* SDWebImageSwiftUIDemo-visionOS.app */; @@ -573,6 +609,9 @@ packageProductDependencies = ( 32B13E842AA368C600BE9B5B /* SDWebImageSwiftUI */, 32B13E862AA368C900BE9B5B /* SDWebImage */, + 32ABE4E02AA3757B00331406 /* SDWebImageWebPCoder */, + 32ABE4E22AA3757B00331406 /* SDWebImageSVGCoder */, + 32ABE4E42AA3757B00331406 /* SDWebImagePDFCoder */, ); productName = "SDWebImageSwiftUIDemo-macOS"; productReference = 32E529092348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS.app */; @@ -594,6 +633,9 @@ packageProductDependencies = ( 32B13E8E2AA368E100BE9B5B /* SDWebImageSwiftUI */, 32B13E902AA368E300BE9B5B /* SDWebImage */, + 32ABE4E62AA3758400331406 /* SDWebImageWebPCoder */, + 32ABE4E82AA3758400331406 /* SDWebImageSVGCoder */, + 32ABE4EA2AA3758400331406 /* SDWebImagePDFCoder */, ); productName = "SDWebImageSwiftUIDemo-tvOS"; productReference = 32E529202348A0D300EA46FF /* SDWebImageSwiftUIDemo-tvOS.app */; @@ -632,8 +674,6 @@ ); name = "SDWebImageSwiftUIDemo-watchOS WatchKit App"; packageProductDependencies = ( - 32B13E922AA368EF00BE9B5B /* SDWebImageSwiftUI */, - 32B13E942AA368F300BE9B5B /* SDWebImage */, ); productName = "SDWebImageSwiftUIDemo-watchOS WatchKit App"; productReference = 32E529372348A0DD00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit App.app */; @@ -652,6 +692,13 @@ dependencies = ( ); name = "SDWebImageSwiftUIDemo-watchOS WatchKit Extension"; + packageProductDependencies = ( + 32ABE4F82AA375A500331406 /* SDWebImage */, + 32ABE4FC2AA375A500331406 /* SDWebImageWebPCoder */, + 32ABE4FE2AA375A500331406 /* SDWebImageSVGCoder */, + 32ABE5002AA375A500331406 /* SDWebImagePDFCoder */, + 32ABE5022AA375B400331406 /* SDWebImageSwiftUI */, + ); productName = "SDWebImageSwiftUIDemo-watchOS WatchKit Extension"; productReference = 32E529462348A0DE00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit Extension.appex */; productType = "com.apple.product-type.watchkit2-extension"; @@ -721,6 +768,9 @@ mainGroup = 607FACC71AFB9204008FA782; packageReferences = ( 3294617A2AA36734009E391B /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */, + 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */, + 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */, + 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */, ); productRefGroup = 607FACD11AFB9204008FA782 /* Products */; projectDirPath = ""; @@ -1852,6 +1902,30 @@ minimumVersion = 2.2.3; }; }; + 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SDWebImage/SDWebImageWebPCoder.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.9.1; + }; + }; + 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SDWebImage/SDWebImageSVGCoder"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; + 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SDWebImage/SDWebImagePDFCoder"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/nalexn/ViewInspector.git"; @@ -1873,6 +1947,89 @@ package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; productName = ViewInspector; }; + 32ABE4D82AA3753300331406 /* SDWebImageWebPCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */; + productName = SDWebImageWebPCoder; + }; + 32ABE4DB2AA3755D00331406 /* SDWebImageSVGCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */; + productName = SDWebImageSVGCoder; + }; + 32ABE4DE2AA3756A00331406 /* SDWebImagePDFCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */; + productName = SDWebImagePDFCoder; + }; + 32ABE4E02AA3757B00331406 /* SDWebImageWebPCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */; + productName = SDWebImageWebPCoder; + }; + 32ABE4E22AA3757B00331406 /* SDWebImageSVGCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */; + productName = SDWebImageSVGCoder; + }; + 32ABE4E42AA3757B00331406 /* SDWebImagePDFCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */; + productName = SDWebImagePDFCoder; + }; + 32ABE4E62AA3758400331406 /* SDWebImageWebPCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */; + productName = SDWebImageWebPCoder; + }; + 32ABE4E82AA3758400331406 /* SDWebImageSVGCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */; + productName = SDWebImageSVGCoder; + }; + 32ABE4EA2AA3758400331406 /* SDWebImagePDFCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */; + productName = SDWebImagePDFCoder; + }; + 32ABE4F22AA3759900331406 /* SDWebImageWebPCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */; + productName = SDWebImageWebPCoder; + }; + 32ABE4F42AA3759900331406 /* SDWebImageSVGCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */; + productName = SDWebImageSVGCoder; + }; + 32ABE4F62AA3759900331406 /* SDWebImagePDFCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */; + productName = SDWebImagePDFCoder; + }; + 32ABE4F82AA375A500331406 /* SDWebImage */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImage; + }; + 32ABE4FC2AA375A500331406 /* SDWebImageWebPCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */; + productName = SDWebImageWebPCoder; + }; + 32ABE4FE2AA375A500331406 /* SDWebImageSVGCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */; + productName = SDWebImageSVGCoder; + }; + 32ABE5002AA375A500331406 /* SDWebImagePDFCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */; + productName = SDWebImagePDFCoder; + }; + 32ABE5022AA375B400331406 /* SDWebImageSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImageSwiftUI; + }; 32B13E802AA368B700BE9B5B /* SDWebImageSwiftUI */ = { isa = XCSwiftPackageProductDependency; productName = SDWebImageSwiftUI; @@ -1901,14 +2058,6 @@ isa = XCSwiftPackageProductDependency; productName = SDWebImage; }; - 32B13E922AA368EF00BE9B5B /* SDWebImageSwiftUI */ = { - isa = XCSwiftPackageProductDependency; - productName = SDWebImageSwiftUI; - }; - 32B13E942AA368F300BE9B5B /* SDWebImage */ = { - isa = XCSwiftPackageProductDependency; - productName = SDWebImage; - }; 32D5D1752A445C8F0098BDFC /* SDWebImageSwiftUI */ = { isa = XCSwiftPackageProductDependency; productName = SDWebImageSwiftUI; diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/AppDelegate.swift b/Example/SDWebImageSwiftUIDemo-visionOS/AppDelegate.swift index 0939fca7..82035803 100644 --- a/Example/SDWebImageSwiftUIDemo-visionOS/AppDelegate.swift +++ b/Example/SDWebImageSwiftUIDemo-visionOS/AppDelegate.swift @@ -9,12 +9,17 @@ import SwiftUI import UIKit import SDWebImage +import SDWebImageWebPCoder +import SDWebImageSVGCoder +import SDWebImagePDFCoder // no changes in your AppDelegate class class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { // Add WebP/SVG/PDF support - SDImageCodersManager.shared.addCoder(SDImageAWebPCoder.shared) + SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared) + SDImageCodersManager.shared.addCoder(SDImageSVGCoder.shared) + SDImageCodersManager.shared.addCoder(SDImagePDFCoder.shared) // Dynamic check to support vector format for both WebImage/AnimatedImage SDWebImageManager.shared.optionsProcessor = SDWebImageOptionsProcessor { url, options, context in var options = options diff --git a/README.md b/README.md index f2d24ed6..155aea30 100644 --- a/README.md +++ b/README.md @@ -618,9 +618,10 @@ struct ContentView : View { To run the example using SwiftUI, following the steps: -1. Run `pod install` on root directory to install the dependency. -2. Open `SDWebImageSwiftUI.xcworkspace`, wait for SwiftPM finishing downloading the test dependency. -3. Choose `SDWebImageSwiftUIDemo` scheme and run the demo application. +1. Open `SDWebImageSwiftUI.xcworkspace`, wait for SwiftPM finishing downloading the test dependency. +2. Choose `SDWebImageSwiftUIDemo` (or other platforms) scheme and run the demo application. + +Note: The `Podfile` here is because history we use CocoaPods to integrate libs into Demo, but now we use SPM. Since SwiftUI is aimed to support all Apple platforms, our demo does this as well, one codebase including: @@ -628,6 +629,7 @@ Since SwiftUI is aimed to support all Apple platforms, our demo does this as wel + macOS + tvOS + watchOS ++ visionOS Demo Tips: From 81810e31af3dc30486b035905585c352c58e61d3 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 2 Sep 2023 22:04:34 +0800 Subject: [PATCH 29/90] Revert the ViewInspector for unit test, fixed version to 0.9.2 --- .../project.pbxproj | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj index 7083664c..5b905c2d 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -59,6 +59,9 @@ 32D5D1722A445BF00098BDFC /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; 32D5D1732A445BF00098BDFC /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; }; 32D5D1762A445C8F0098BDFC /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32D5D1752A445C8F0098BDFC /* SDWebImageSwiftUI */; }; + 32DAC2392AA3784800A085AE /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 32DAC2382AA3784800A085AE /* ViewInspector */; }; + 32DAC23B2AA3784D00A085AE /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 32DAC23A2AA3784D00A085AE /* ViewInspector */; }; + 32DAC23D2AA3785100A085AE /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 32DAC23C2AA3785100A085AE /* ViewInspector */; }; 32E5290C2348A0C700EA46FF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E5290B2348A0C700EA46FF /* AppDelegate.swift */; }; 32E529102348A0C900EA46FF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32E5290F2348A0C900EA46FF /* Assets.xcassets */; }; 32E529132348A0C900EA46FF /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32E529122348A0C900EA46FF /* Preview Assets.xcassets */; }; @@ -218,6 +221,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 32DAC2392AA3784800A085AE /* ViewInspector in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -225,6 +229,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 32DAC23B2AA3784D00A085AE /* ViewInspector in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -232,6 +237,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 32DAC23D2AA3785100A085AE /* ViewInspector in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -522,6 +528,7 @@ ); name = SDWebImageSwiftUITests; packageProductDependencies = ( + 32DAC2382AA3784800A085AE /* ViewInspector */, ); productName = SDWebImageSwiftUITests; productReference = 322E0DE628D3318B0003A55F /* SDWebImageSwiftUITests.xctest */; @@ -543,6 +550,7 @@ ); name = "SDWebImageSwiftUITests macOS"; packageProductDependencies = ( + 32DAC23A2AA3784D00A085AE /* ViewInspector */, ); productName = "SDWebImageSwiftUITests macOS"; productReference = 322E0E0228D331F00003A55F /* SDWebImageSwiftUITests macOS.xctest */; @@ -564,6 +572,7 @@ ); name = "SDWebImageSwiftUITests tvOS"; packageProductDependencies = ( + 32DAC23C2AA3785100A085AE /* ViewInspector */, ); productName = "SDWebImageSwiftUITests tvOS"; productReference = 322E0E0F28D332050003A55F /* SDWebImageSwiftUITests tvOS.xctest */; @@ -771,6 +780,7 @@ 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */, 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */, 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */, + 32B13EB12AA377F600BE9B5B /* XCRemoteSwiftPackageReference "ViewInspector" */, ); productRefGroup = 607FACD11AFB9204008FA782 /* Products */; projectDirPath = ""; @@ -1926,6 +1936,14 @@ minimumVersion = 1.0.0; }; }; + 32B13EB12AA377F600BE9B5B /* XCRemoteSwiftPackageReference "ViewInspector" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/nalexn/ViewInspector.git"; + requirement = { + kind = exactVersion; + version = 0.9.2; + }; + }; 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/nalexn/ViewInspector.git"; @@ -2062,6 +2080,21 @@ isa = XCSwiftPackageProductDependency; productName = SDWebImageSwiftUI; }; + 32DAC2382AA3784800A085AE /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 32B13EB12AA377F600BE9B5B /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; + 32DAC23A2AA3784D00A085AE /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 32B13EB12AA377F600BE9B5B /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; + 32DAC23C2AA3785100A085AE /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 32B13EB12AA377F600BE9B5B /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; 32DCFE9628D333F1001A17BF /* ViewInspector */ = { isa = XCSwiftPackageProductDependency; package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; From 0e88e1433965d5434f9d31bcf567017c936c9823 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 2 Sep 2023 22:11:32 +0800 Subject: [PATCH 30/90] Update the readme for visionOS platform --- README.md | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 155aea30..e6c55c38 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,11 @@ The framework provide the different View structs, which API match the SwiftUI fr ## Apple VisionOS -SDWebImageSwiftUI can compiled for visionOS platform. However, due to the package manager support, we can not integrate using the exists function. +From v3.0.0 (beta), SDWebImageSwiftUI can be compiled for visionOS platform. However, due to the lacking package manager support (need tools update), we don't support CocoaPods/SPM yet. -So, in [visionOS branch](https://github.com/SDWebImage/SDWebImageSwiftUI/tree/feature/visionOS), our demo use the Xcode's built-in dependency to build. +You can only use the Xcode's built-in package manager dependency to build on visionOS. -You can build and run to see the example (still need improvement) +To run the visionOS example, you need to clone and add both `SDWebImage` and `SDWebImageSwiftUI`, open the `SDWebImageSwiftUI.xcworkspace` and drag those folders to become local package dependency, see: [Editing a package dependency as a local package](https://developer.apple.com/documentation/xcode/editing-a-package-dependency-as-a-local-package) ## Features @@ -58,32 +58,17 @@ All issue reports, feature requests, contributions, and GitHub stars are welcome ## Requirements -+ Xcode 12+ ++ Xcode 14+ + iOS 14+ + macOS 11+ + tvOS 14+ + watchOS 7+ -## SwiftUI 2.0 Compatibility +## for SwiftUI 1.0 (iOS 13) iOS 14(macOS 11) introduce the SwiftUI 2.0, which keep the most API compatible, but changes many internal behaviors, which breaks the SDWebImageSwiftUI's function. -From v2.0.0, we adopt SwiftUI 2.0 and iOS 14(macOS 11)'s behavior. You can use `WebImage` and `AnimatedImage` inside the new `LazyVStack`. - -```swift -var body: some View { - ScrollView { - LazyVStack { - ForEach(urls, id: \.self) { url in - AnimatedImage(url: url) - } - } - } -} -``` - -Note: However, many differences behavior between iOS 13/14 is hard to fixup. Due to maintain issue, from SDWebImageSwiftUI v3.0, iOS 13 is no longer supported. We always match SwiftUI 2.0's behavior. - +From v3.0.0 (Beta), SDWebImageSwiftUI drop iOS 13 support. To use on iOS 13, checkout the latest v2.x version (or using `2.x` branch) instead. ## Installation From 75fff6c46ee6dd0e839405b2091d31eba942ba63 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 2 Sep 2023 22:22:04 +0800 Subject: [PATCH 31/90] Update github action to Xcode 14 && macOS 12 environment --- .github/workflows/CI.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index eb220a2c..8be7306b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -14,9 +14,9 @@ permissions: jobs: Pods: name: Cocoapods Lint - runs-on: macos-11 + runs-on: macos-12 env: - DEVELOPER_DIR: /Applications/Xcode_13.2.1.app + DEVELOPER_DIR: /Applications/Xcode_14.1.app steps: - name: Checkout uses: actions/checkout@v2 @@ -34,9 +34,9 @@ jobs: Demo: name: Run Demo - runs-on: macos-11 + runs-on: macos-12 env: - DEVELOPER_DIR: /Applications/Xcode_13.2.1.app + DEVELOPER_DIR: /Applications/Xcode_14.1.app WORKSPACE_NAME: SDWebImageSwiftUI.xcworkspace OSXSCHEME: SDWebImageSwiftUIDemo-macOS iOSSCHEME: SDWebImageSwiftUIDemo @@ -45,7 +45,7 @@ jobs: strategy: matrix: iosDestination: ["name=iPhone 13 Pro"] - tvOSDestination: ["name=Apple TV 4K"] + tvOSDestination: ["name=Apple TV"] watchOSDestination: ["platform=watchOS Simulator,name=Apple Watch Series 7 - 45mm"] macOSDestination: ["platform=macOS"] macCatalystDestination: ["platform=macOS,arch=x86_64,variant=Mac Catalyst"] @@ -92,9 +92,9 @@ jobs: Test: name: Unit Test - runs-on: macos-11 + runs-on: macos-12 env: - DEVELOPER_DIR: /Applications/Xcode_13.2.1.app + DEVELOPER_DIR: /Applications/Xcode_14.1.app WORKSPACE_NAME: SDWebImageSwiftUI.xcworkspace OSXSCHEME: SDWebImageSwiftUITests macOS iOSSCHEME: SDWebImageSwiftUITests @@ -154,9 +154,9 @@ jobs: Build: name: Build Library - runs-on: macos-11 + runs-on: macos-12 env: - DEVELOPER_DIR: /Applications/Xcode_13.2.1.app + DEVELOPER_DIR: /Applications/Xcode_14.1.app PROJECT_NAME: SDWebImageSwiftUI.xcodeproj OSXSCHEME: SDWebImageSwiftUI macOS iOSSCHEME: SDWebImageSwiftUI From 15990ac34f6cdaac0b9b5ecb27918437c1b2b230 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 2 Sep 2023 22:43:10 +0800 Subject: [PATCH 32/90] Added the temp Carthage support to build visionOS framework Fix some new warning on Swift 5.9 --- README.md | 6 + SDWebImageSwiftUI.xcodeproj/project.pbxproj | 194 ++++++++++++++++-- SDWebImageSwiftUI/Classes/ImageManager.swift | 1 + SDWebImageSwiftUI/Classes/ImagePlayer.swift | 1 + .../Classes/Indicator/Indicator.swift | 2 +- 5 files changed, 181 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index e6c55c38..e749b607 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,12 @@ You can only use the Xcode's built-in package manager dependency to build on vis To run the visionOS example, you need to clone and add both `SDWebImage` and `SDWebImageSwiftUI`, open the `SDWebImageSwiftUI.xcworkspace` and drag those folders to become local package dependency, see: [Editing a package dependency as a local package](https://developer.apple.com/documentation/xcode/editing-a-package-dependency-as-a-local-package) +If you really want to build framework instead of using Xcode's package dependency, following the manual steps below: + +1. Clone SDWebImage, open `SDWebImage.xcodeproj` and build `SDWebImage` target for visionOS platform (Change `MACH_O_TYPE` to static library if you need) +2. Clone SDWebImageSwiftUI, create directory at `Carthage/Build/visionOS` and copy `SDWebImage.framework` into it +3. Open `SDWebImageSwiftUI.xcodeproj` and build `SDWebImageSwiftUI visionOS` target + ## Features Since SDWebImageSwiftUI is built on top of SDWebImage, it provide both the out-of-box features as well as advanced powerful features you may want in real world Apps. Check our [Wiki](https://github.com/SDWebImage/SDWebImage/wiki/Advanced-Usage) when you need: diff --git a/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/SDWebImageSwiftUI.xcodeproj/project.pbxproj index 1a2286cf..d3e5a45d 100644 --- a/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -7,22 +7,25 @@ objects = { /* Begin PBXBuildFile section */ + 3243AFE62AA37EFF0049A43B /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */; }; + 3243AFE72AA37EFF0049A43B /* WebImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43DDE22FD54C600BE87F5 /* WebImage.swift */; }; + 3243AFE82AA37EFF0049A43B /* ImagePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */; }; + 3243AFE92AA37EFF0049A43B /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; }; + 3243AFEA2AA37EFF0049A43B /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; }; + 3243AFEB2AA37EFF0049A43B /* AnimatedImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43DDF22FD54C600BE87F5 /* AnimatedImage.swift */; }; + 3243AFEC2AA37EFF0049A43B /* ImageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43DDC22FD54C600BE87F5 /* ImageManager.swift */; }; + 3243AFED2AA37EFF0049A43B /* SDWebImageSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */; }; + 3243AFEE2AA37F010049A43B /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; }; + 3243AFEF2AA37F030049A43B /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; 326B84822363350C0011BDFB /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; }; 326B84832363350C0011BDFB /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; }; 326B84842363350C0011BDFB /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; }; 326B84852363350C0011BDFB /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; }; - 326B8487236335110011BDFB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B8486236335110011BDFB /* ActivityIndicator.swift */; }; - 326B8488236335110011BDFB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B8486236335110011BDFB /* ActivityIndicator.swift */; }; - 326B8489236335110011BDFB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B8486236335110011BDFB /* ActivityIndicator.swift */; }; - 326B848A236335110011BDFB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B8486236335110011BDFB /* ActivityIndicator.swift */; }; - 326B848C236335400011BDFB /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B848B236335400011BDFB /* ProgressIndicator.swift */; }; - 326B848D236335400011BDFB /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B848B236335400011BDFB /* ProgressIndicator.swift */; }; - 326B848E236335400011BDFB /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B848B236335400011BDFB /* ProgressIndicator.swift */; }; - 326B848F236335400011BDFB /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B848B236335400011BDFB /* ProgressIndicator.swift */; }; 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 */; }; + 329885EE2AA37FCB0071F2BA /* SDWebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 329885ED2AA37FCB0071F2BA /* SDWebImage.framework */; }; 32B79C9528DB40430088C432 /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */; }; 32B79C9628DB40430088C432 /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */; }; 32B79C9728DB40430088C432 /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */; }; @@ -71,13 +74,12 @@ 3211F84F23DE98E300FC757F /* WebImageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebImageTests.swift; sourceTree = ""; }; 3211F85423DE9D2700FC757F /* Images.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Images.bundle; sourceTree = ""; }; 322E0F4723E57F09006836DC /* TestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUtils.swift; sourceTree = ""; }; + 3243AFDF2AA37EE90049A43B /* SDWebImageSwiftUI_visionOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImageSwiftUI_visionOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 326B84812363350C0011BDFB /* Indicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Indicator.swift; sourceTree = ""; }; - 326B8486236335110011BDFB /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; - 326B848B236335400011BDFB /* ProgressIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressIndicator.swift; sourceTree = ""; }; 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewWrapper.swift; sourceTree = ""; }; + 329885ED2AA37FCB0071F2BA /* SDWebImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDWebImage.framework; path = Carthage/Build/visionOS/SDWebImage.framework; sourceTree = ""; }; 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUICompatibility.swift; sourceTree = ""; }; 32B933E423659A1900BB7CAD /* Transition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transition.swift; sourceTree = ""; }; - 32BD9C4623E03B08008D5F6A /* IndicatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndicatorTests.swift; sourceTree = ""; }; 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 = ""; }; 32C43DDE22FD54C600BE87F5 /* WebImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebImage.swift; sourceTree = ""; }; @@ -98,6 +100,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 3243AFDC2AA37EE90049A43B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 329885EE2AA37FCB0071F2BA /* SDWebImage.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 32C43DC922FD540D00BE87F5 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -140,7 +150,6 @@ 3211F84823DE984D00FC757F /* Info.plist */, 3211F84623DE984D00FC757F /* AnimatedImageTests.swift */, 3211F84F23DE98E300FC757F /* WebImageTests.swift */, - 32BD9C4623E03B08008D5F6A /* IndicatorTests.swift */, 32ED4825242A13030053338E /* ImageManagerTests.swift */, 322E0F4723E57F09006836DC /* TestUtils.swift */, ); @@ -151,8 +160,6 @@ isa = PBXGroup; children = ( 326B84812363350C0011BDFB /* Indicator.swift */, - 326B8486236335110011BDFB /* ActivityIndicator.swift */, - 326B848B236335400011BDFB /* ProgressIndicator.swift */, ); path = Indicator; sourceTree = ""; @@ -184,6 +191,7 @@ 32C43DF422FD57FD00BE87F5 /* SDWebImageSwiftUI.framework */, 32C43E0122FD581400BE87F5 /* SDWebImageSwiftUI.framework */, 32C43E0E22FD581C00BE87F5 /* SDWebImageSwiftUI.framework */, + 3243AFDF2AA37EE90049A43B /* SDWebImageSwiftUI_visionOS.framework */, ); name = Products; sourceTree = ""; @@ -216,6 +224,7 @@ 32C43DE822FD577300BE87F5 /* Frameworks */ = { isa = PBXGroup; children = ( + 329885ED2AA37FCB0071F2BA /* SDWebImage.framework */, 32C43E2D22FD586E00BE87F5 /* SDWebImage.framework */, 32C43E2922FD586200BE87F5 /* SDWebImage.framework */, 32C43E2522FD585300BE87F5 /* SDWebImage.framework */, @@ -227,6 +236,13 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 3243AFDA2AA37EE90049A43B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 32C43DC722FD540D00BE87F5 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -262,6 +278,24 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 3243AFDE2AA37EE90049A43B /* SDWebImageSwiftUI visionOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3243AFE32AA37EEA0049A43B /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUI visionOS" */; + buildPhases = ( + 3243AFDA2AA37EE90049A43B /* Headers */, + 3243AFDB2AA37EE90049A43B /* Sources */, + 3243AFDC2AA37EE90049A43B /* Frameworks */, + 3243AFDD2AA37EE90049A43B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "SDWebImageSwiftUI visionOS"; + productName = "SDWebImageSwiftUI visionOS"; + productReference = 3243AFDF2AA37EE90049A43B /* SDWebImageSwiftUI_visionOS.framework */; + productType = "com.apple.product-type.framework"; + }; 32C43DCB22FD540D00BE87F5 /* SDWebImageSwiftUI */ = { isa = PBXNativeTarget; buildConfigurationList = 32C43DD422FD540D00BE87F5 /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUI" */; @@ -344,6 +378,9 @@ LastUpgradeCheck = 1100; ORGANIZATIONNAME = SDWebImage; TargetAttributes = { + 3243AFDE2AA37EE90049A43B = { + CreatedOnToolsVersion = 15.0; + }; 32C43DCB22FD540D00BE87F5 = { CreatedOnToolsVersion = 11.0; LastSwiftMigration = 1100; @@ -379,11 +416,19 @@ 32C43DF322FD57FD00BE87F5 /* SDWebImageSwiftUI macOS */, 32C43E0022FD581400BE87F5 /* SDWebImageSwiftUI tvOS */, 32C43E0D22FD581C00BE87F5 /* SDWebImageSwiftUI watchOS */, + 3243AFDE2AA37EE90049A43B /* SDWebImageSwiftUI visionOS */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 3243AFDD2AA37EE90049A43B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 32C43DCA22FD540D00BE87F5 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -415,6 +460,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 3243AFDB2AA37EE90049A43B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3243AFEF2AA37F030049A43B /* Transition.swift in Sources */, + 3243AFE92AA37EFF0049A43B /* ImageViewWrapper.swift in Sources */, + 3243AFE72AA37EFF0049A43B /* WebImage.swift in Sources */, + 3243AFEC2AA37EFF0049A43B /* ImageManager.swift in Sources */, + 3243AFEB2AA37EFF0049A43B /* AnimatedImage.swift in Sources */, + 3243AFE82AA37EFF0049A43B /* ImagePlayer.swift in Sources */, + 3243AFED2AA37EFF0049A43B /* SDWebImageSwiftUI.swift in Sources */, + 3243AFE62AA37EFF0049A43B /* SwiftUICompatibility.swift in Sources */, + 3243AFEE2AA37F010049A43B /* Indicator.swift in Sources */, + 3243AFEA2AA37EFF0049A43B /* Image.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 32C43DC822FD540D00BE87F5 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -422,12 +484,10 @@ 32B933E523659A1900BB7CAD /* Transition.swift in Sources */, 32CBA78025E4D7D800C6A8DC /* ImagePlayer.swift in Sources */, 32C43E1722FD583700BE87F5 /* WebImage.swift in Sources */, - 326B848C236335400011BDFB /* ProgressIndicator.swift in Sources */, 326B84822363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3222FD5DE100BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480A23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, 32B79C9528DB40430088C432 /* SwiftUICompatibility.swift in Sources */, - 326B8487236335110011BDFB /* ActivityIndicator.swift in Sources */, 32C43E1622FD583700BE87F5 /* ImageManager.swift in Sources */, 32C43E1822FD583700BE87F5 /* AnimatedImage.swift in Sources */, 32D26A022446B546005905DA /* Image.swift in Sources */, @@ -441,12 +501,10 @@ 32B933E623659A1900BB7CAD /* Transition.swift in Sources */, 32CBA78125E4D7D800C6A8DC /* ImagePlayer.swift in Sources */, 32C43E1A22FD583700BE87F5 /* WebImage.swift in Sources */, - 326B848D236335400011BDFB /* ProgressIndicator.swift in Sources */, 326B84832363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3322FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, 32B79C9628DB40430088C432 /* SwiftUICompatibility.swift in Sources */, - 326B8488236335110011BDFB /* ActivityIndicator.swift in Sources */, 32C43E1922FD583700BE87F5 /* ImageManager.swift in Sources */, 32C43E1B22FD583700BE87F5 /* AnimatedImage.swift in Sources */, 32D26A032446B546005905DA /* Image.swift in Sources */, @@ -460,12 +518,10 @@ 32B933E723659A1900BB7CAD /* Transition.swift in Sources */, 32CBA78225E4D7D800C6A8DC /* ImagePlayer.swift in Sources */, 32C43E1D22FD583800BE87F5 /* WebImage.swift in Sources */, - 326B848E236335400011BDFB /* ProgressIndicator.swift in Sources */, 326B84842363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3422FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, 32B79C9728DB40430088C432 /* SwiftUICompatibility.swift in Sources */, - 326B8489236335110011BDFB /* ActivityIndicator.swift in Sources */, 32C43E1C22FD583800BE87F5 /* ImageManager.swift in Sources */, 32C43E1E22FD583800BE87F5 /* AnimatedImage.swift in Sources */, 32D26A042446B546005905DA /* Image.swift in Sources */, @@ -479,12 +535,10 @@ 32B933E823659A1900BB7CAD /* Transition.swift in Sources */, 32CBA78325E4D7D800C6A8DC /* ImagePlayer.swift in Sources */, 32C43E2022FD583800BE87F5 /* WebImage.swift in Sources */, - 326B848F236335400011BDFB /* ProgressIndicator.swift in Sources */, 326B84852363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3522FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, 32B79C9828DB40430088C432 /* SwiftUICompatibility.swift in Sources */, - 326B848A236335110011BDFB /* ActivityIndicator.swift in Sources */, 32C43E1F22FD583800BE87F5 /* ImageManager.swift in Sources */, 32C43E2122FD583800BE87F5 /* AnimatedImage.swift in Sources */, 32D26A052446B546005905DA /* Image.swift in Sources */, @@ -494,6 +548,91 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ + 3243AFE42AA37EEA0049A43B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/visionOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 SDWebImage. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUI-visionOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = xros; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + }; + name = Debug; + }; + 3243AFE52AA37EEA0049A43B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/visionOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 SDWebImage. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUI-visionOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = xros; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + }; + name = Release; + }; 32C43DD222FD540D00BE87F5 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -564,6 +703,7 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WATCHOS_DEPLOYMENT_TARGET = 7.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Debug; }; @@ -631,6 +771,7 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WATCHOS_DEPLOYMENT_TARGET = 7.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Release; }; @@ -875,6 +1016,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 3243AFE32AA37EEA0049A43B /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUI visionOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3243AFE42AA37EEA0049A43B /* Debug */, + 3243AFE52AA37EEA0049A43B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 32C43DC622FD540D00BE87F5 /* Build configuration list for PBXProject "SDWebImageSwiftUI" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index 2c134ce7..008b0a39 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -7,6 +7,7 @@ */ import SwiftUI +import Combine import SDWebImage /// A Image observable object for handle image load process. This drive the Source of Truth for image loading status. diff --git a/SDWebImageSwiftUI/Classes/ImagePlayer.swift b/SDWebImageSwiftUI/Classes/ImagePlayer.swift index c00f8e92..8e5820c8 100644 --- a/SDWebImageSwiftUI/Classes/ImagePlayer.swift +++ b/SDWebImageSwiftUI/Classes/ImagePlayer.swift @@ -7,6 +7,7 @@ */ import SwiftUI +import Combine import SDWebImage /// A Image observable object for handle aniamted image playback. This is used to avoid `@State` update may capture the View struct type and cause memory leak. diff --git a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift index 76113ced..26f0162a 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift @@ -6,8 +6,8 @@ * file that was distributed with this source code. */ -import Foundation import SwiftUI +import Combine /// A type to build the indicator @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) From 1a17ef9dc07f6b1146f32de5b9f881ea9c10bb54 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 2 Sep 2023 22:56:21 +0800 Subject: [PATCH 33/90] Released v3.0.0-beta version Update the CHANGELOG.md --- CHANGELOG.md | 12 ++++++++++++ SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a81c8f7..2d8f81ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.0.0-beta] - 2023-09-02 + +### Added +- (Part 1) Support compile for visionOS (no package manager support) #267 + +### Changed + +- Drop iOS 13/macOS 10.15/tvOS 13/watchOS 6 support #250 +- ProgressIndicator and ActivityIndicator is removed. Use `ProgressView` instead +- Availability is changed to iOS 14/macOS 11/tvOS 11/watchOS 7 +- Embed `SwiftUIBackports` dependency is removed. + ## [2.2.3] - 2023-04-32 - Fix the issue that Static Library + Library Evolution cause the build issue on Swift 5.8 #263 diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index d1a7ac85..5d9ca8ff 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '2.2.3' + s.version = '3.0.0-beta' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index 49c664c0..4dbb7412 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.2.3 + 3.0.0-beta CFBundleVersion $(CURRENT_PROJECT_VERSION) From 86b1901e4017afd628e17aef35194c18cdff08d4 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 2 Sep 2023 23:08:13 +0800 Subject: [PATCH 34/90] Added the missing shared xcscheme for visionOS --- .../SDWebImageSwiftUI visionOS.xcscheme | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUI visionOS.xcscheme diff --git a/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUI visionOS.xcscheme b/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUI visionOS.xcscheme new file mode 100644 index 00000000..def8a061 --- /dev/null +++ b/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUI visionOS.xcscheme @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + From 9ec9e29e1400bc51ca43caef73f8bf4056e0ed07 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 20 Sep 2023 18:08:31 +0800 Subject: [PATCH 35/90] Update the WebImage API to match SwiftUI.AsyncImage (not SwiftUI.Image), make it more easy to replace The old API is still kept, except the .placeholder one --- .../SDWebImageSwiftUIDemo/DetailView.swift | 24 ++-- SDWebImageSwiftUI/Classes/WebImage.swift | 131 +++++++++--------- 2 files changed, 85 insertions(+), 70 deletions(-) diff --git a/Example/SDWebImageSwiftUIDemo/DetailView.swift b/Example/SDWebImageSwiftUIDemo/DetailView.swift index 3501c8d3..4406f553 100644 --- a/Example/SDWebImageSwiftUIDemo/DetailView.swift +++ b/Example/SDWebImageSwiftUIDemo/DetailView.swift @@ -101,18 +101,26 @@ struct DetailView: View { .indicator(SDWebImageProgressIndicator.default) .scaledToFit() #else - WebImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating) - .resizable() - .placeholder(.wifiExclamationmark) + WebImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating) { image in + image.resizable() + .scaledToFit() + } placeholder: { + Image.wifiExclamationmark + .resizable() + .scaledToFit() + } .indicator(.progress) - .scaledToFit() #endif } else { - WebImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating) - .resizable() - .placeholder(.wifiExclamationmark) + WebImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating) { image in + image.resizable() + .scaledToFit() + } placeholder: { + Image.wifiExclamationmark + .resizable() + .scaledToFit() + } .indicator(.progress(style: .circular)) - .scaledToFit() } } } diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index bc6a7ef9..457b6e63 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -9,6 +9,43 @@ import SwiftUI import SDWebImage +public enum WebImagePhase { + /// No image is loaded. + case empty + + /// An image succesfully loaded. + case success(Image) + + /// An image failed to load with an error. + case failure(Error) + + /// The loaded image, if any. + /// + /// If this value isn't `nil`, the image load operation has finished, + /// and you can use the image to update the view. You can use the image + /// directly, or you can modify it in some way. For example, you can add + /// a ``Image/resizable(capInsets:resizingMode:)`` modifier to make the + /// image resizable. + public var image: Image? { + switch self { + case let .success(image): + return image + case .empty, .failure: + return nil + } + } + + /// The error that occurred when attempting to load an image, if any. + public var error: Error? { + switch self { + case .empty, .success: + return nil + case let .failure(error): + return error + } + } +} + /// Data Binding Object, only properties in this object can support changes from user with @State and refresh @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) final class WebImageModel : ObservableObject { @@ -43,10 +80,12 @@ final class WebImageConfiguration: ObservableObject { /// A Image View type to load image from url. Supports static/animated image format. @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -public struct WebImage : View { +public struct WebImage : View where Content: View { + var transaction: Transaction + var configurations: [(Image) -> Image] = [] - var placeholder: AnyView? + var content: (WebImagePhase) -> Content /// A Binding to control the animation. You can bind external logic to control the animation status. /// True to start animation, false to stop animation. @@ -72,7 +111,23 @@ public struct WebImage : View { /// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values. /// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. /// - Parameter isAnimating: The binding for animation control. The binding value should be `true` when initialized to setup the correct animated image class. If not, you must provide the `.animatedImageClass` explicitly. When the animation started, this binding can been used to start / stop the animation. - public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding) { + public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true)) where Content == Image { + self.init(url: url, options: options, context: context, isAnimating: isAnimating) { phase in + phase.image ?? Image(platformImage: .empty) + } + } + + public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), @ViewBuilder content: @escaping (Image) -> I, @ViewBuilder placeholder: @escaping () -> P) where Content == _ConditionalContent, I: View, P: View { + self.init(url: url, options: options, context: context, isAnimating: isAnimating) { phase in + if let i = phase.image { + content(i) + } else { + placeholder() + } + } + } + + public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), transaction: Transaction = Transaction(), @ViewBuilder content: @escaping (WebImagePhase) -> Content) { self._isAnimating = isAnimating var context = context ?? [:] // provide animated image class if the initialized `isAnimating` is true, user can still custom the image class if they want @@ -89,27 +144,16 @@ public struct WebImage : View { let imageManager = ImageManager() _imageManager = StateObject(wrappedValue: imageManager) _indicatorStatus = ObservedObject(wrappedValue: imageManager.indicatorStatus) - } - - /// Create a web image with url, placeholder, custom options and context. - /// - Parameter url: The image url - /// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values. - /// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. - public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) { - self.init(url: url, options: options, context: context, isAnimating: .constant(true)) + + self.transaction = transaction + self.content = { phase in + content(phase) + } } public var body: some View { // Container return ZStack { - // This empty Image is used to receive container's level appear/disappear to start/stop player, reduce CPU usage - Image(platformImage: .empty) - .onAppear { - self.appearAction() - } - .onDisappear { - self.disappearAction() - } // Render Logic for actual animated image frame or static image if imageManager.image != nil && imageModel.url == imageManager.currentURL { if isAnimating && !imageManager.isIncremental { @@ -118,8 +162,8 @@ public struct WebImage : View { displayImage() } } else { + content((imageManager.error != nil) ? .failure(imageManager.error!) : .empty) // Load Logic - setupPlaceholder() .onPlatformAppear(appear: { self.setupManager() if (self.imageManager.error == nil) { @@ -145,7 +189,7 @@ public struct WebImage : View { /// Configure the platform image into the SwiftUI rendering image func configure(image: PlatformImage) -> some View { // Actual rendering SwiftUI image - let result: Image + var result: Image // NSImage works well with SwiftUI, include Vector and EXIF images. #if os(macOS) result = Image(nsImage: image) @@ -188,9 +232,12 @@ public struct WebImage : View { // Should not use `EmptyView`, which does not respect to the container's frame modifier // Using a empty image instead for better compatible - return configurations.reduce(result) { (previous, configuration) in + let i = configurations.reduce(result) { (previous, configuration) in configuration(previous) } + + // Apply view builder + return content(.success(i)) } /// Image Manager status @@ -279,25 +326,6 @@ public struct WebImage : View { } } } - - /// Placeholder View Support - func setupPlaceholder() -> some View { - // Don't use `Group` because it will trigger `.onAppear` and `.onDisappear` when condition view removed, treat placeholder as an entire component - let result: AnyView - if let placeholder = placeholder { - // If use `.delayPlaceholder`, the placeholder is applied after loading failed, hide during loading :) - if imageModel.options.contains(.delayPlaceholder) && imageManager.error == nil { - result = AnyView(configure(image: .empty)) - } else { - result = placeholder - } - } else { - result = AnyView(configure(image: .empty)) - } - // Custom ID to avoid SwiftUI engine cache the status, and does not call `onAppear` when placeholder not changed (See `ContentView.swift/ContentView2` case) - // Because we load the image url in placeholder's `onAppear`, it should be called to sync with state changes :) - return result.id(imageModel.url) - } } // Layout @@ -373,27 +401,6 @@ extension WebImage { // WebImage Modifier @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { - - /// Associate a placeholder when loading image with url - /// - note: The differences between Placeholder and Indicator, is that placeholder does not supports animation, and return type is different - /// - Parameter content: A view that describes the placeholder. - public func placeholder(@ViewBuilder content: () -> T) -> WebImage where T : View { - var result = self - result.placeholder = AnyView(content()) - return result - } - - /// Associate a placeholder image when loading image with url - /// - note: This placeholder image will apply the same size and resizable from WebImage for convenience. If you don't want this, use the ViewBuilder one above instead - /// - Parameter image: A Image view that describes the placeholder. - public func placeholder(_ image: Image) -> WebImage { - return placeholder { - configurations.reduce(image) { (previous, configuration) in - configuration(previous) - } - } - } - /// Control the behavior to retry the failed loading when view become appears again /// - Parameter flag: Whether or not to retry the failed loading public func retryOnAppear(_ flag: Bool) -> WebImage { From a94221fba0bc0787637e6a00bb6b716f46bdc633 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 21 Sep 2023 18:02:36 +0800 Subject: [PATCH 36/90] Update the API of AnimatedImage as well 1. Change the placeholder into Web URL init method (placeholder not works for data/bundle init method) 2. Add convenient .progress/.activity syntax for AnimatedImage indicator --- .../SDWebImageSwiftUIDemo/ContentView.swift | 16 +-- .../SDWebImageSwiftUIDemo/DetailView.swift | 5 +- README.md | 26 ++--- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 109 +++++++----------- Tests/AnimatedImageTests.swift | 10 +- Tests/WebImageTests.swift | 10 +- 6 files changed, 66 insertions(+), 110 deletions(-) diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index e75e6afb..6dc50ad5 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -96,22 +96,19 @@ struct ContentView: View { HStack { if self.animated { #if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) - AnimatedImage(url: URL(string:url), isAnimating: .constant(true)) + AnimatedImage(url: URL(string:url)) .onViewUpdate { view, context in #if os(macOS) view.toolTip = url #endif } - .indicator(SDWebImageActivityIndicator.medium) - /** - .placeholder(UIImage(systemName: "photo")) - */ + .indicator(.activity) .transition(.fade) .resizable() .scaledToFit() .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) #else - WebImage(url: URL(string:url), isAnimating: self.$animated) + WebImage(url: URL(string:url)) .resizable() .indicator(.activity) .transition(.fade(duration: 0.5)) @@ -119,13 +116,8 @@ struct ContentView: View { .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) #endif } else { - WebImage(url: URL(string:url), isAnimating: .constant(true)) + WebImage(url: URL(string:url)) .resizable() - /** - .placeholder { - Image(systemName: "photo") - } - */ .indicator(.activity) .transition(.fade(duration: 0.5)) .scaledToFit() diff --git a/Example/SDWebImageSwiftUIDemo/DetailView.swift b/Example/SDWebImageSwiftUIDemo/DetailView.swift index 4406f553..35621a76 100644 --- a/Example/SDWebImageSwiftUIDemo/DetailView.swift +++ b/Example/SDWebImageSwiftUIDemo/DetailView.swift @@ -95,10 +95,9 @@ struct DetailView: View { HStack { if animated { #if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) - AnimatedImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating) + AnimatedImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating, placeholderImage: .wifiExclamationmark) + .indicator(.progress) .resizable() - .placeholder(.wifiExclamationmark) - .indicator(SDWebImageProgressIndicator.default) .scaledToFit() #else WebImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating) { image in diff --git a/README.md b/README.md index e749b607..0fef4b1f 100644 --- a/README.md +++ b/README.md @@ -128,18 +128,16 @@ github "SDWebImage/SDWebImageSwiftUI" ```swift var body: some View { - WebImage(url: URL(string: "https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic")) + WebImage(url: URL(string: "https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic")) { image in + image.resizable() // Control layout like SwiftUI.AsyncImage, you must use this modifier or the view will use the image bitmap size + } placeholder: { + Rectangle().foregroundColor(.gray) + } // Supports options and context, like `.delayPlaceholder` to show placeholder only when error .onSuccess { image, data, cacheType in // Success // Note: Data exist only when queried from disk cache or network. Use `.queryMemoryData` if you really need data } - .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size - .placeholder(Image(systemName: "photo")) // Placeholder Image - // Supports ViewBuilder as well - .placeholder { - Rectangle().foregroundColor(.gray) - } .indicator(.activity) // Activity Indicator .transition(.fade(duration: 0.5)) // Fade Transition with duration .scaledToFit() @@ -194,21 +192,21 @@ WebImage(url: url) ```swift var body: some View { Group { - AnimatedImage(url: URL(string: "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif")) + AnimatedImage(url: URL(string: "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif"), placeholderImage: .init(systemName: "photo")) // Placeholder Image // Supports options and context, like `.progressiveLoad` for progressive animation loading .onFailure { error in // Error } .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size - .placeholder(UIImage(systemName: "photo")) // Placeholder Image - // Supports ViewBuilder as well - .placeholder { - Circle().foregroundColor(.gray) - } - .indicator(SDWebImageActivityIndicator.medium) // Activity Indicator + .indicator(.activity) // Activity Indicator .transition(.fade) // Fade Transition .scaledToFit() // Attention to call it on AnimatedImage, but not `some View` after View Modifier (Swift Protocol Extension method is static dispatched) + // Supports SwiftUI ViewBuilder placeholder as well + AnimatedImage(url: url) { + Circle().foregroundColor(.gray) + } + // Data AnimatedImage(data: try! Data(contentsOf: URL(fileURLWithPath: "/tmp/foo.webp"))) .customLoopCount(1) // Custom loop count diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index ef187e20..0e2f5c03 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -31,6 +31,12 @@ final class AnimatedImageModel : ObservableObject { @Published var url: URL? @Published var webOptions: SDWebImageOptions = [] @Published var webContext: [SDWebImageContextOption : Any]? = nil + @Published var placeholderImage: PlatformImage? + @Published var placeholderView: PlatformView? { + didSet { + oldValue?.removeFromSuperview() + } + } /// Name image @Published var name: String? @Published var bundle: Bundle? @@ -90,12 +96,6 @@ final class AnimatedImageConfiguration: ObservableObject { // These configurations only useful for web image loading var indicator: SDWebImageIndicator? var transition: SDWebImageTransition? - var placeholder: PlatformImage? - var placeholderView: PlatformView? { - didSet { - oldValue?.removeFromSuperview() - } - } } /// A Image View type to load image from url, data or bundle. Supports animated and static image format. @@ -115,13 +115,19 @@ public struct AnimatedImage : PlatformViewRepresentable { /// True to start animation, false to stop animation. @Binding public var isAnimating: Bool - /// Create an animated image with url, placeholder, custom options and context. + /// Create an animated image with url, placeholder, custom options and context, including animation control binding. /// - Parameter url: The image url /// - Parameter placeholder: The placeholder image to show during loading /// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values. /// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. - public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) { - self.init(url: url, options: options, context: context, isAnimating: .constant(true)) + /// - Parameter isAnimating: The binding for animation control + public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), placeholderImage: PlatformImage? = nil) { + let imageModel = AnimatedImageModel() + imageModel.url = url + imageModel.webOptions = options + imageModel.webContext = context + imageModel.placeholderImage = placeholderImage + self.init(imageModel: imageModel, isAnimating: isAnimating) } /// Create an animated image with url, placeholder, custom options and context, including animation control binding. @@ -130,46 +136,37 @@ public struct AnimatedImage : PlatformViewRepresentable { /// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values. /// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. /// - Parameter isAnimating: The binding for animation control - public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding) { + public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), @ViewBuilder placeholder: @escaping () -> T) where T : View { let imageModel = AnimatedImageModel() imageModel.url = url imageModel.webOptions = options imageModel.webContext = context + #if os(macOS) + let hostingView = NSHostingView(rootView: placeholder()) + #else + let hostingView = _UIHostingView(rootView: placeholder()) + #endif + imageModel.placeholderView = hostingView self.init(imageModel: imageModel, isAnimating: isAnimating) } - /// Create an animated image with name and bundle. - /// - Note: Asset Catalog is not supported. - /// - Parameter name: The image name - /// - Parameter bundle: The bundle contains image - public init(name: String, bundle: Bundle? = nil) { - self.init(name: name, bundle: bundle, isAnimating: .constant(true)) - } - /// Create an animated image with name and bundle, including animation control binding. /// - Note: Asset Catalog is not supported. /// - Parameter name: The image name /// - Parameter bundle: The bundle contains image /// - Parameter isAnimating: The binding for animation control - public init(name: String, bundle: Bundle? = nil, isAnimating: Binding) { + public init(name: String, bundle: Bundle? = nil, isAnimating: Binding = .constant(true)) { let imageModel = AnimatedImageModel() imageModel.name = name imageModel.bundle = bundle self.init(imageModel: imageModel, isAnimating: isAnimating) } - /// Create an animated image with data and scale. - /// - Parameter data: The image data - /// - Parameter scale: The scale factor - public init(data: Data, scale: CGFloat = 1) { - self.init(data: data, scale: scale, isAnimating: .constant(true)) - } - /// Create an animated image with data and scale, including animation control binding. /// - Parameter data: The image data /// - Parameter scale: The scale factor /// - Parameter isAnimating: The binding for animation control - public init(data: Data, scale: CGFloat = 1, isAnimating: Binding) { + public init(data: Data, scale: CGFloat = 1, isAnimating: Binding = .constant(true)) { let imageModel = AnimatedImageModel() imageModel.data = data imageModel.scale = scale @@ -222,7 +219,7 @@ public struct AnimatedImage : PlatformViewRepresentable { func setupIndicator(_ view: AnimatedImageViewWrapper, context: Context) { view.wrapped.sd_imageIndicator = imageConfiguration.indicator view.wrapped.sd_imageTransition = imageConfiguration.transition - if let placeholderView = imageConfiguration.placeholderView { + if let placeholderView = imageModel.placeholderView { placeholderView.removeFromSuperview() placeholderView.isHidden = true // Placeholder View should below the Indicator View @@ -243,13 +240,13 @@ public struct AnimatedImage : PlatformViewRepresentable { context.coordinator.imageLoading.isLoading = true let webOptions = imageModel.webOptions if webOptions.contains(.delayPlaceholder) { - self.imageConfiguration.placeholderView?.isHidden = true + self.imageModel.placeholderView?.isHidden = true } else { - self.imageConfiguration.placeholderView?.isHidden = false + self.imageModel.placeholderView?.isHidden = false } var webContext = imageModel.webContext ?? [:] webContext[.animatedImageClass] = SDAnimatedImage.self - view.wrapped.sd_internalSetImage(with: imageModel.url, placeholderImage: imageConfiguration.placeholder, options: webOptions, context: webContext, setImageBlock: nil, progress: { (receivedSize, expectedSize, _) in + view.wrapped.sd_internalSetImage(with: imageModel.url, placeholderImage: imageModel.placeholderImage, options: webOptions, context: webContext, setImageBlock: nil, progress: { (receivedSize, expectedSize, _) in let progress: Double if (expectedSize > 0) { progress = Double(receivedSize) / Double(expectedSize) @@ -265,10 +262,10 @@ public struct AnimatedImage : PlatformViewRepresentable { context.coordinator.imageLoading.isLoading = false context.coordinator.imageLoading.progress = 1 if let image = image { - self.imageConfiguration.placeholderView?.isHidden = true + self.imageModel.placeholderView?.isHidden = true self.imageHandler.successBlock?(image, data, cacheType) } else { - self.imageConfiguration.placeholderView?.isHidden = false + self.imageModel.placeholderView?.isHidden = false self.imageHandler.failureBlock?(error ?? NSError()) } } @@ -780,30 +777,19 @@ extension AnimatedImage { } } +// Convenient indicator dot syntax +extension SDWebImageIndicator where Self == SDWebImageActivityIndicator { + public static var activity: Self { Self() } +} + +extension SDWebImageIndicator where Self == SDWebImageProgressIndicator { + public static var progress: Self { Self() } +} + // Web Image convenience, based on UIKit/AppKit API @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { - /// Associate a placeholder when loading image with url - /// - Parameter content: A view that describes the placeholder. - /// - note: The differences between this and placeholder image, it's that placeholder image replace the image for image view, but this modify the View Hierarchy to overlay the placeholder hosting view - public func placeholder(@ViewBuilder content: () -> T) -> AnimatedImage where T : View { - #if os(macOS) - let hostingView = NSHostingView(rootView: content()) - #else - let hostingView = _UIHostingView(rootView: content()) - #endif - self.imageConfiguration.placeholderView = hostingView - return self - } - - /// Associate a placeholder image when loading image with url - /// - Parameter content: A view that describes the placeholder. - public func placeholder(_ image: PlatformImage?) -> AnimatedImage { - self.imageConfiguration.placeholder = image - return self - } - /// Associate a indicator when loading image with url /// - Note: If you do not need indicator, specify nil. Defaults to nil /// - Parameter indicator: indicator, see more in `SDWebImageIndicator` @@ -821,23 +807,6 @@ extension AnimatedImage { } } -// Indicator -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -extension AnimatedImage { - - /// Associate a indicator when loading image with url - /// - Parameter indicator: The indicator type, see `Indicator` - public func indicator(_ indicator: Indicator) -> some View where T : View { - return self.modifier(IndicatorViewModifier(status: indicatorStatus, indicator: indicator)) - } - - /// Associate a indicator when loading image with url, convenient method with block - /// - Parameter content: A view that describes the indicator. - public func indicator(@ViewBuilder content: @escaping (_ isAnimating: Binding, _ progress: Binding) -> T) -> some View where T : View { - return indicator(Indicator(content: content)) - } -} - #if DEBUG @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) struct AnimatedImage_Previews : PreviewProvider { diff --git a/Tests/AnimatedImageTests.swift b/Tests/AnimatedImageTests.swift index 29472033..613539e8 100644 --- a/Tests/AnimatedImageTests.swift +++ b/Tests/AnimatedImageTests.swift @@ -142,7 +142,9 @@ class AnimatedImageTests: XCTestCase { func testAnimatedImageModifier() throws { let expectation = self.expectation(description: "WebImage modifier") let imageUrl = URL(string: "https://assets.sbnation.com/assets/2512203/dogflops.gif") - let imageView = AnimatedImage(url: imageUrl, options: [.progressiveLoad], context: [.imageScaleFactor: 1]) + let imageView = AnimatedImage(url: imageUrl, options: [.progressiveLoad], context: [.imageScaleFactor: 1]) { + Circle() + } let introspectView = imageView .onSuccess { _, _, _ in expectation.fulfill() @@ -161,11 +163,7 @@ class AnimatedImageTests: XCTestCase { XCTAssert(view.isKind(of: SDAnimatedImageView.self)) XCTAssertEqual(context.coordinator.userInfo?["foo"] as? String, "bar") } - .placeholder(PlatformImage()) - .placeholder { - Circle() - } - .indicator(SDWebImageActivityIndicator.medium) + .indicator(.activity) // Image .resizable() .renderingMode(.original) diff --git a/Tests/WebImageTests.swift b/Tests/WebImageTests.swift index d51efdbc..36090720 100644 --- a/Tests/WebImageTests.swift +++ b/Tests/WebImageTests.swift @@ -73,7 +73,11 @@ class WebImageTests: XCTestCase { func testWebImageModifier() throws { let expectation = self.expectation(description: "WebImage modifier") let imageUrl = URL(string: "https://raw.githubusercontent.com/ibireme/YYImage/master/Demo/YYImageDemo/mew_baseline.jpg") - let imageView = WebImage(url: imageUrl, options: [.progressiveLoad], context: [.imageScaleFactor: 1]) + let imageView = WebImage(url: imageUrl, options: [.progressiveLoad], context: [.imageScaleFactor: 1]) { image in + image.resizable() + } placeholder: { + Circle() + } let introspectView = imageView .onSuccess { _, _, _ in expectation.fulfill() @@ -83,10 +87,6 @@ class WebImageTests: XCTestCase { } .onProgress { _, _ in - } - .placeholder(.init(platformImage: PlatformImage())) - .placeholder { - Circle() } // Image .resizable() From 69c6f008dc17128dbcf788f967bc9e843d486ee3 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 21 Sep 2023 22:38:58 +0800 Subject: [PATCH 37/90] Update the watchOS demo without the force-touch --- Example/SDWebImageSwiftUIDemo/ContentView.swift | 17 +++++++++-------- Example/SDWebImageSwiftUIDemo/DetailView.swift | 6 ++---- README.md | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index 6dc50ad5..ba4215da 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -191,15 +191,16 @@ struct ContentView: View { } #endif #if os(watchOS) - return contentView() - .contextMenu { - Button(action: { self.reloadCache() }) { - Text("Reload") - } - Button(action: { self.switchView() }) { - Text("Switch") + return NavigationView { + contentView() + .navigationTitle("WebImage") + .toolbar { + Button(action: { self.reloadCache() }) { + Text("Reload") + } } - } + + } #endif } diff --git a/Example/SDWebImageSwiftUIDemo/DetailView.swift b/Example/SDWebImageSwiftUIDemo/DetailView.swift index 35621a76..a56d3511 100644 --- a/Example/SDWebImageSwiftUIDemo/DetailView.swift +++ b/Example/SDWebImageSwiftUIDemo/DetailView.swift @@ -52,10 +52,8 @@ struct DetailView: View { #endif #if os(macOS) || os(watchOS) zoomView() - .contextMenu { - Button(isAnimating ? "Stop" : "Start") { - self.isAnimating.toggle() - } + .onTapGesture { + self.isAnimating.toggle() } #endif } diff --git a/README.md b/README.md index 0fef4b1f..676548c8 100644 --- a/README.md +++ b/README.md @@ -622,8 +622,8 @@ Since SwiftUI is aimed to support all Apple platforms, our demo does this as wel Demo Tips: -1. Use `Switch` (right-click on macOS/force press on watchOS) to switch between `WebImage` and `AnimatedImage`. -2. Use `Reload` (right-click on macOS/force press on watchOS) to clear cache. +1. Use `Switch` (right-click on macOS/tap on watchOS) to switch between `WebImage` and `AnimatedImage`. +2. Use `Reload` (right-click on macOS/button on watchOS) to clear cache. 3. Use `Swipe Left` (menu button on tvOS) to delete one image url from list. 4. Pinch gesture (Digital Crown on watchOS, play button on tvOS) to zoom-in detail page image. 5. Clear cache and go to detail page to see progressive loading. From e96aaa90cd77280cd97cf322ee6af89193125914 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 21 Sep 2023 23:26:19 +0800 Subject: [PATCH 38/90] Update the github actions --- .github/workflows/CI.yml | 2 +- .../SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8be7306b..58fa29f1 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -46,7 +46,7 @@ jobs: matrix: iosDestination: ["name=iPhone 13 Pro"] tvOSDestination: ["name=Apple TV"] - watchOSDestination: ["platform=watchOS Simulator,name=Apple Watch Series 7 - 45mm"] + watchOSDestination: ["platform=watchOS Simulator,name=Apple Watch Series 7 (45mm)"] macOSDestination: ["platform=macOS"] macCatalystDestination: ["platform=macOS,arch=x86_64,variant=Mac Catalyst"] steps: diff --git a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme index 65df20bc..58b52ed0 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme +++ b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme @@ -53,7 +53,8 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - allowLocationSimulation = "YES"> + allowLocationSimulation = "YES" + notificationPayloadFile = "PushNotificationPayload.apns"> Date: Sat, 21 Oct 2023 16:23:18 +0800 Subject: [PATCH 39/90] Allows to use UIImage/NSImage as defaults when init the AnimatedImage with JPEG data This match the behavior when passing with the JPEG url initializer --- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index ef187e20..832e0328 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -286,15 +286,29 @@ public struct AnimatedImage : PlatformViewRepresentable { // Refresh image, imageModel is the Source of Truth, switch the type // Although we have Source of Truth, we can check the previous value, to avoid re-generate SDAnimatedImage, which is performance-cost. if let name = imageModel.name, name != context.coordinator.imageLoading.imageName { + var image: PlatformImage? #if os(macOS) - let image = SDAnimatedImage(named: name, in: imageModel.bundle) + image = SDAnimatedImage(named: name, in: imageModel.bundle) + if image == nil { + // For static image, use NSImage as defaults + let bundle = imageModel.bundle ?? .main + image = bundle.image(forResource: name) + } #else - let image = SDAnimatedImage(named: name, in: imageModel.bundle, compatibleWith: nil) + image = SDAnimatedImage(named: name, in: imageModel.bundle, compatibleWith: nil) + if image == nil { + // For static image, use UIImage as defaults + image = PlatformImage(named: name, in: imageModel.bundle, compatibleWith: nil) + } #endif context.coordinator.imageLoading.imageName = name view.wrapped.image = image } else if let data = imageModel.data, data != context.coordinator.imageLoading.imageData { - let image = SDAnimatedImage(data: data, scale: imageModel.scale) + var image: PlatformImage? = SDAnimatedImage(data: data, scale: imageModel.scale) + if image == nil { + // For static image, use UIImage as defaults + image = PlatformImage(data: data) + } context.coordinator.imageLoading.imageData = data view.wrapped.image = image } else if let url = imageModel.url { From 932b33b6bd9481c9b56843d6fdff076f93e6d15d Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 21 Sep 2023 23:26:19 +0800 Subject: [PATCH 40/90] Update the github actions --- .github/workflows/CI.yml | 2 +- .../SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8be7306b..58fa29f1 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -46,7 +46,7 @@ jobs: matrix: iosDestination: ["name=iPhone 13 Pro"] tvOSDestination: ["name=Apple TV"] - watchOSDestination: ["platform=watchOS Simulator,name=Apple Watch Series 7 - 45mm"] + watchOSDestination: ["platform=watchOS Simulator,name=Apple Watch Series 7 (45mm)"] macOSDestination: ["platform=macOS"] macCatalystDestination: ["platform=macOS,arch=x86_64,variant=Mac Catalyst"] steps: diff --git a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme index 65df20bc..58b52ed0 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme +++ b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme @@ -53,7 +53,8 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - allowLocationSimulation = "YES"> + allowLocationSimulation = "YES" + notificationPayloadFile = "PushNotificationPayload.apns"> Date: Sat, 21 Oct 2023 16:51:21 +0800 Subject: [PATCH 41/90] Should use the SDWebImage's static image decode API instead of UIKit's one --- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 832e0328..6283f568 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -307,7 +307,7 @@ public struct AnimatedImage : PlatformViewRepresentable { var image: PlatformImage? = SDAnimatedImage(data: data, scale: imageModel.scale) if image == nil { // For static image, use UIImage as defaults - image = PlatformImage(data: data) + image = PlatformImage.sd_image(with: data, scale: imageModel.scale) } context.coordinator.imageLoading.imageData = data view.wrapped.image = image From e45f290a77a3c8c0bba4d8e027632d1bc378cef7 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 21 Oct 2023 18:37:21 +0800 Subject: [PATCH 42/90] Released v3.0.0-beta.2 version Update the CHANGELOG.md --- CHANGELOG.md | 13 +++++++++++++ SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d8f81ce..44349541 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] + +## [3.0.0-beta.2] - 2023-10-21 + +### Changed +- Update the WebImage API to match SwiftUI.AsyncImage #275 @Kyle-Ye +- Allows to use UIImage/NSImage as defaults when init the AnimatedImage with JPEG data #277 + +### Removed +- `WebImage.placeholder(@ViewBuilder content: () -> T) -> WebImage` +- `WebImage.placeholder(_ image: Image) -> WebImage` +- `AnimatedImage.placeholder(@ViewBuilder content: () -> T) -> AnimatedImage` +- `AnimatedImage.placeholder(_ image: PlatformImage) -> AnimatedImage` + ## [3.0.0-beta] - 2023-09-02 ### Added diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 5d9ca8ff..c9612f07 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '3.0.0-beta' + s.version = '3.0.0-beta.2' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index 4dbb7412..f4b0a0bb 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 3.0.0-beta + 3.0.0-beta.2 CFBundleVersion $(CURRENT_PROJECT_VERSION) From ed081436eb1be6b00720d6f22803511ff1109736 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 29 Nov 2023 00:25:53 +0800 Subject: [PATCH 43/90] Update README.md Added tutorial about SVG/PDF with tint color using WebImage/AnimatedImage --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 676548c8..74fc91d3 100644 --- a/README.md +++ b/README.md @@ -419,6 +419,26 @@ NavigationView { } ``` +#### Render vector image (SVG/PDF) with tint color + +Both `WebImage/AnimatedImage` supports to render the vector image, by using the `SVG/PDF` external coders. However they are different internally. + ++ `AnimatedImage`: use tech from Apple's symbol image and vector drawing, supports dynamic size changes without lossing details. And it use UIKit/AppKit based implementation and APIs. If you want, pass `.context(.imageThumbnailPixelSize: size)` to use bitmap rendering and get more pixels. ++ `WebImage`: draws vector image into a bitmap version. Which just like normal PNG. By default, we use vector image content size (SVG canvas size or PDF media box size). If you want, pass `.context(.imageThumbnailPixelSize: size)` to get more pixels. + +For `WebImage` (or bitmap rendering on `AnimatedImage`), you can also tint the SVG/PDF icons with custom colors (like symbol images), use the `.renderingMode(.template)` and `.foregroundColor(color)` modifier, which matches `SwiftUI.Image` behavior. + +```swift +var body: some View { + WebImage(url: URL(string: "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/w3c.svg")) + .resizable() + .renderingMode(.template) + .foregroundColor(.red) + .scaledToFit() +} +``` + +See more: [Configuring and displaying symbol images in your UI](https://developer.apple.com/documentation/uikit/uiimage/configuring_and_displaying_symbol_images_in_your_ui?language=objc) #### Using with external loaders/caches/coders From 11f2fba5171f4cb6cd363574fb296386ae4b1ece Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 29 Nov 2023 01:05:19 +0800 Subject: [PATCH 44/90] Update the readme about the tint color for AnimatedImage Fix the implementation that breaks the compatible with SDWebImage 5.18+ --- README.md | 22 ++++++++++++++++--- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 74fc91d3..100638a5 100644 --- a/README.md +++ b/README.md @@ -231,7 +231,7 @@ Note: `AnimatedImage` supports both image url or image data for animated image f Note: `AnimatedImage` some methods like `.transition`, `.indicator` and `.aspectRatio` have the same naming as `SwiftUI.View` protocol methods. But the args receive the different type. This is because `AnimatedImage` supports to be used with UIKit/AppKit component and animation. If you find ambiguity, use full type declaration instead of the dot expression syntax. -Note: some of methods on `AnimatedImage` will return `some View`, a new Modified Content. You'll lose the type related modifier method. For this case, you can either reorder the method call, or use Native View in `.onViewUpdate` for rescue. +Note: some of methods on `AnimatedImage` will return `some View`, a new Modified Content. You'll lose the type related modifier method. For this case, you can either reorder the method call, or use native view (actually `SDAnimatedImageView`) in `.onViewUpdate`, use UIKIt/AppKit API for rescue. ```swift @@ -426,14 +426,30 @@ Both `WebImage/AnimatedImage` supports to render the vector image, by using the + `AnimatedImage`: use tech from Apple's symbol image and vector drawing, supports dynamic size changes without lossing details. And it use UIKit/AppKit based implementation and APIs. If you want, pass `.context(.imageThumbnailPixelSize: size)` to use bitmap rendering and get more pixels. + `WebImage`: draws vector image into a bitmap version. Which just like normal PNG. By default, we use vector image content size (SVG canvas size or PDF media box size). If you want, pass `.context(.imageThumbnailPixelSize: size)` to get more pixels. -For `WebImage` (or bitmap rendering on `AnimatedImage`), you can also tint the SVG/PDF icons with custom colors (like symbol images), use the `.renderingMode(.template)` and `.foregroundColor(color)` modifier, which matches `SwiftUI.Image` behavior. +For bitmap rendering, you can also tint the SVG/PDF icons with custom colors (like symbol images), use the `.renderingMode(.template)` and `.tint(:)` or `.foregroundColor(:)` modifier, which matches `SwiftUI.Image` behavior. + ++ WebImage ```swift var body: some View { WebImage(url: URL(string: "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/w3c.svg")) .resizable() .renderingMode(.template) - .foregroundColor(.red) + .foregroundColor(.red) // or `.tint(:)`, `.accentColor(:)` + .scaledToFit() +} +``` + ++ AnimatedImage + +```swift +var body: some View { + AnimatedImage(url: URL(string: "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/w3c.svg"), context: [.imageThumbnailPixelSize : CGSize(width: 100, height: 100)]) + .resizable() + .renderingMode(.template) + // seems `.foregroundColor(:)` does effect `UIView.tintColor`, use `tint(:)` or `.accentColor(:)` instead. + // Or you can use `onViewCreate(:)` to get native `SDAnimatedImageView` and set `tintColor` (AppKit use `contentTintColor`) + .tint(.red) .scaledToFit() } ``` diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 4a4498ef..48f8f309 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -418,7 +418,7 @@ public struct AnimatedImage : PlatformViewRepresentable { } // Animated Image does not support resizing mode and rendering mode - if let image = view.wrapped.image, !image.conforms(to: SDAnimatedImageProtocol.self) { + if let image = view.wrapped.image { var image = image // ResizingMode if let resizingMode = imageLayout.resizingMode, imageLayout.capInsets != EdgeInsets() { From 6bd9811577eed28c1269cabb7898f8798c5955fa Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 29 Nov 2023 19:02:41 +0800 Subject: [PATCH 45/90] Fix the Unit test again because of ViewInspector version --- Tests/WebImageTests.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Tests/WebImageTests.swift b/Tests/WebImageTests.swift index 36090720..bb2ae18c 100644 --- a/Tests/WebImageTests.swift +++ b/Tests/WebImageTests.swift @@ -23,9 +23,9 @@ class WebImageTests: XCTestCase { let imageView = WebImage(url: imageUrl) let introspectView = imageView.onSuccess { image, data, cacheType in #if os(macOS) - let displayImage = try? imageView.inspect().zStack().image(1).actualImage().nsImage() + let displayImage = try? imageView.inspect().zStack().image(0).actualImage().nsImage() #else - let displayImage = try? imageView.inspect().zStack().image(1).actualImage().cgImage() + let displayImage = try? imageView.inspect().zStack().image(0).actualImage().cgImage() #endif XCTAssertNotNil(displayImage) expectation.fulfill() @@ -47,10 +47,10 @@ class WebImageTests: XCTestCase { if let animatedImage = image as? SDAnimatedImage { XCTAssertTrue(imageView.isAnimating) #if os(macOS) - let displayImage = try? imageView.inspect().zStack().image(1).actualImage().nsImage() + let displayImage = try? imageView.inspect().zStack().image(0).actualImage().nsImage() let size = displayImage?.size #else - let displayImage = try? imageView.inspect().zStack().image(1).actualImage().cgImage() + let displayImage = try? imageView.inspect().zStack().image(0).actualImage().cgImage() let size = CGSize(width: displayImage?.width ?? 0, height: displayImage?.height ?? 0) #endif XCTAssertNotNil(displayImage) @@ -161,11 +161,11 @@ class WebImageTests: XCTestCase { let imageView = WebImage(url: imageUrl) let introspectView = imageView.onSuccess { image, data, cacheType in #if os(macOS) - let displayImage = try? imageView.inspect().zStack().image(1).actualImage().nsImage() + let displayImage = try? imageView.inspect().zStack().image(0).actualImage().nsImage() XCTAssertNotNil(displayImage) #else - let displayImage = try? imageView.inspect().zStack().image(1).actualImage().cgImage() - let orientation = try? imageView.inspect().zStack().image(1).actualImage().orientation() + let displayImage = try? imageView.inspect().zStack().image(0).actualImage().cgImage() + let orientation = try? imageView.inspect().zStack().image(0).actualImage().orientation() XCTAssertNotNil(displayImage) XCTAssertEqual(orientation, .leftMirrored) #endif From a448bbe47d3aa626cb10df8a41cc0ec3544701c1 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 29 Nov 2023 01:18:37 +0800 Subject: [PATCH 46/90] Update the AnimatedImage API to expose the SDAnimatedImageView This is not necessary to hide the details and use UIView super view --- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 12 ++++++------ SDWebImageSwiftUI/Classes/ImageViewWrapper.swift | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 48f8f309..1a6adbf8 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -66,8 +66,8 @@ final class AnimatedImageHandler: ObservableObject { @Published var failureBlock: ((Error) -> Void)? @Published var progressBlock: ((Int, Int) -> Void)? // Coordinator Handler - @Published var viewCreateBlock: ((PlatformView, AnimatedImage.Context) -> Void)? - @Published var viewUpdateBlock: ((PlatformView, AnimatedImage.Context) -> Void)? + @Published var viewCreateBlock: ((SDAnimatedImageView, AnimatedImage.Context) -> Void)? + @Published var viewUpdateBlock: ((SDAnimatedImageView, AnimatedImage.Context) -> Void)? } /// Layout Binding Object, supports dynamic @State changes @@ -109,7 +109,7 @@ public struct AnimatedImage : PlatformViewRepresentable { /// A observed object to pass through the image manager loading status to indicator @ObservedObject var indicatorStatus = IndicatorStatus() - static var viewDestroyBlock: ((PlatformView, Coordinator) -> Void)? + static var viewDestroyBlock: ((SDAnimatedImageView, Coordinator) -> Void)? /// A Binding to control the animation. You can bind external logic to control the animation status. /// True to start animation, false to stop animation. @@ -770,7 +770,7 @@ extension AnimatedImage { /// Provide the action when view representable create the native view. /// - Parameter action: The action to perform. The first arg is the native view. The seconds arg is the context. /// - Returns: A view that triggers `action` when view representable create the native view. - public func onViewCreate(perform action: ((PlatformView, Context) -> Void)? = nil) -> AnimatedImage { + public func onViewCreate(perform action: ((SDAnimatedImageView, Context) -> Void)? = nil) -> AnimatedImage { self.imageHandler.viewCreateBlock = action return self } @@ -778,7 +778,7 @@ extension AnimatedImage { /// Provide the action when view representable update the native view. /// - Parameter action: The action to perform. The first arg is the native view. The seconds arg is the context. /// - Returns: A view that triggers `action` when view representable update the native view. - public func onViewUpdate(perform action: ((PlatformView, Context) -> Void)? = nil) -> AnimatedImage { + public func onViewUpdate(perform action: ((SDAnimatedImageView, Context) -> Void)? = nil) -> AnimatedImage { self.imageHandler.viewUpdateBlock = action return self } @@ -786,7 +786,7 @@ extension AnimatedImage { /// Provide the action when view representable destroy the native view /// - Parameter action: The action to perform. The first arg is the native view. The seconds arg is the coordinator (with userInfo). /// - Returns: A view that triggers `action` when view representable destroy the native view. - public static func onViewDestroy(perform action: ((PlatformView, Coordinator) -> Void)? = nil) { + public static func onViewDestroy(perform action: ((SDAnimatedImageView, Coordinator) -> Void)? = nil) { self.viewDestroyBlock = action } } diff --git a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift index 68b12afc..b0d6ac35 100644 --- a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift +++ b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift @@ -14,12 +14,13 @@ import SDWebImage /// Use wrapper to solve tne `UIImageView`/`NSImageView` frame size become image size issue (SwiftUI's Bug) @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public class AnimatedImageViewWrapper : PlatformView { - var wrapped = SDAnimatedImageView() + /// The wrapped actual image view, using SDWebImage's aniamted image view + public var wrapped = SDAnimatedImageView() var interpolationQuality = CGInterpolationQuality.default var shouldAntialias = false var resizable = false - override public func draw(_ rect: CGRect) { + public override func draw(_ rect: CGRect) { #if os(macOS) guard let ctx = NSGraphicsContext.current?.cgContext else { return From 71ce58eec4412c17a824a4cad8db4d4814ce3117 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 29 Nov 2023 16:10:17 +0800 Subject: [PATCH 47/90] Little code garden --- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 1a6adbf8..7e0248fb 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -178,11 +178,7 @@ public struct AnimatedImage : PlatformViewRepresentable { _imageModel = ObservedObject(wrappedValue: imageModel) } - #if os(macOS) - public typealias NSViewType = AnimatedImageViewWrapper - #else - public typealias UIViewType = AnimatedImageViewWrapper - #endif + public typealias PlatformViewType = AnimatedImageViewWrapper public typealias Coordinator = AnimatedImageCoordinator From e120c3bb61781db5086d9f2e25b3920360bd2901 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 30 Nov 2023 15:18:02 +0800 Subject: [PATCH 48/90] Debug CI for unit test --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 58fa29f1..7544522b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -134,7 +134,7 @@ jobs: - name: Test - ${{ matrix.macOSDestination }} run: | set -o pipefail - xcodebuild test -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.OSXSCHEME }}" -destination "${{ matrix.macOSDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c + xcodebuild test -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.OSXSCHEME }}" -destination "${{ matrix.macOSDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO mv ~/Library/Developer/Xcode/DerivedData/ ./DerivedData/macOS - name: Test - ${{ matrix.tvOSDestination }} From 23d1d3b2a0112524988d17abe36d9f3cf8b6c7b3 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 30 Nov 2023 15:54:05 +0800 Subject: [PATCH 49/90] Fix unit test xcode project integration --- .../project.pbxproj | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj index 5b905c2d..2a26b45d 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -41,18 +41,13 @@ 32ABE4F32AA3759900331406 /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4F22AA3759900331406 /* SDWebImageWebPCoder */; }; 32ABE4F52AA3759900331406 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4F42AA3759900331406 /* SDWebImageSVGCoder */; }; 32ABE4F72AA3759900331406 /* SDWebImagePDFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4F62AA3759900331406 /* SDWebImagePDFCoder */; }; - 32ABE4F92AA375A500331406 /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4F82AA375A500331406 /* SDWebImage */; }; 32ABE4FD2AA375A500331406 /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4FC2AA375A500331406 /* SDWebImageWebPCoder */; }; 32ABE4FF2AA375A500331406 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4FE2AA375A500331406 /* SDWebImageSVGCoder */; }; 32ABE5012AA375A500331406 /* SDWebImagePDFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE5002AA375A500331406 /* SDWebImagePDFCoder */; }; 32ABE5032AA375B400331406 /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE5022AA375B400331406 /* SDWebImageSwiftUI */; }; 32B13E812AA368B700BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E802AA368B700BE9B5B /* SDWebImageSwiftUI */; }; - 32B13E832AA368B900BE9B5B /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E822AA368B900BE9B5B /* SDWebImage */; }; 32B13E852AA368C600BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E842AA368C600BE9B5B /* SDWebImageSwiftUI */; }; - 32B13E872AA368C900BE9B5B /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E862AA368C900BE9B5B /* SDWebImage */; }; - 32B13E892AA368CC00BE9B5B /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E882AA368CC00BE9B5B /* SDWebImage */; }; 32B13E8F2AA368E100BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E8E2AA368E100BE9B5B /* SDWebImageSwiftUI */; }; - 32B13E912AA368E300BE9B5B /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E902AA368E300BE9B5B /* SDWebImage */; }; 32D5D1672A445B260098BDFC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5D1662A445B260098BDFC /* AppDelegate.swift */; }; 32D5D16B2A445B260098BDFC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32D5D16A2A445B260098BDFC /* Assets.xcassets */; }; 32D5D16E2A445B260098BDFC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32D5D16D2A445B260098BDFC /* Preview Assets.xcassets */; }; @@ -169,7 +164,6 @@ 322E0E0228D331F00003A55F /* SDWebImageSwiftUITests macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SDWebImageSwiftUITests macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 322E0E0F28D332050003A55F /* SDWebImageSwiftUITests tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SDWebImageSwiftUITests tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 326B0D702345C01900D28269 /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = ""; }; - 3294617D2AA36759009E391B /* SDWebImage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SDWebImage; path = ../../SDWebImage; sourceTree = ""; }; 3294617E2AA36761009E391B /* SDWebImageSwiftUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SDWebImageSwiftUI; path = ..; sourceTree = ""; }; 32D5D1602A445B250098BDFC /* SDWebImageSwiftUIDemo-visionOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SDWebImageSwiftUIDemo-visionOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 32D5D1662A445B260098BDFC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; wrapsLines = 0; }; @@ -209,7 +203,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 32B13E892AA368CC00BE9B5B /* SDWebImage in Frameworks */, 32D5D1762A445C8F0098BDFC /* SDWebImageSwiftUI in Frameworks */, 32ABE4D92AA3753300331406 /* SDWebImageWebPCoder in Frameworks */, 32ABE4DF2AA3756A00331406 /* SDWebImagePDFCoder in Frameworks */, @@ -245,7 +238,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 32B13E832AA368B900BE9B5B /* SDWebImage in Frameworks */, 32B13E812AA368B700BE9B5B /* SDWebImageSwiftUI in Frameworks */, 32ABE4F32AA3759900331406 /* SDWebImageWebPCoder in Frameworks */, 32ABE4F72AA3759900331406 /* SDWebImagePDFCoder in Frameworks */, @@ -257,7 +249,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 32B13E872AA368C900BE9B5B /* SDWebImage in Frameworks */, 32B13E852AA368C600BE9B5B /* SDWebImageSwiftUI in Frameworks */, 32ABE4E12AA3757B00331406 /* SDWebImageWebPCoder in Frameworks */, 32ABE4E52AA3757B00331406 /* SDWebImagePDFCoder in Frameworks */, @@ -269,7 +260,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 32B13E912AA368E300BE9B5B /* SDWebImage in Frameworks */, 32B13E8F2AA368E100BE9B5B /* SDWebImageSwiftUI in Frameworks */, 32ABE4E72AA3758400331406 /* SDWebImageWebPCoder in Frameworks */, 32ABE4EB2AA3758400331406 /* SDWebImagePDFCoder in Frameworks */, @@ -282,7 +272,6 @@ buildActionMask = 2147483647; files = ( 32ABE5032AA375B400331406 /* SDWebImageSwiftUI in Frameworks */, - 32ABE4F92AA375A500331406 /* SDWebImage in Frameworks */, 32ABE4FD2AA375A500331406 /* SDWebImageWebPCoder in Frameworks */, 32ABE5012AA375A500331406 /* SDWebImagePDFCoder in Frameworks */, 32ABE4FF2AA375A500331406 /* SDWebImageSVGCoder in Frameworks */, @@ -443,7 +432,6 @@ 607FACC71AFB9204008FA782 = { isa = PBXGroup; children = ( - 3294617D2AA36759009E391B /* SDWebImage */, 3294617E2AA36761009E391B /* SDWebImageSwiftUI */, 607FACF51AFB993E008FA782 /* Podspec Metadata */, 320CDC2A22FADB44007CF858 /* SDWebImageSwiftUIDemo */, @@ -503,7 +491,6 @@ name = SDWebImageSwiftUIDemo; packageProductDependencies = ( 32D5D1752A445C8F0098BDFC /* SDWebImageSwiftUI */, - 32B13E882AA368CC00BE9B5B /* SDWebImage */, 32ABE4D82AA3753300331406 /* SDWebImageWebPCoder */, 32ABE4DB2AA3755D00331406 /* SDWebImageSVGCoder */, 32ABE4DE2AA3756A00331406 /* SDWebImagePDFCoder */, @@ -593,7 +580,6 @@ name = "SDWebImageSwiftUIDemo-visionOS"; packageProductDependencies = ( 32B13E802AA368B700BE9B5B /* SDWebImageSwiftUI */, - 32B13E822AA368B900BE9B5B /* SDWebImage */, 32ABE4F22AA3759900331406 /* SDWebImageWebPCoder */, 32ABE4F42AA3759900331406 /* SDWebImageSVGCoder */, 32ABE4F62AA3759900331406 /* SDWebImagePDFCoder */, @@ -617,7 +603,6 @@ name = "SDWebImageSwiftUIDemo-macOS"; packageProductDependencies = ( 32B13E842AA368C600BE9B5B /* SDWebImageSwiftUI */, - 32B13E862AA368C900BE9B5B /* SDWebImage */, 32ABE4E02AA3757B00331406 /* SDWebImageWebPCoder */, 32ABE4E22AA3757B00331406 /* SDWebImageSVGCoder */, 32ABE4E42AA3757B00331406 /* SDWebImagePDFCoder */, @@ -641,7 +626,6 @@ name = "SDWebImageSwiftUIDemo-tvOS"; packageProductDependencies = ( 32B13E8E2AA368E100BE9B5B /* SDWebImageSwiftUI */, - 32B13E902AA368E300BE9B5B /* SDWebImage */, 32ABE4E62AA3758400331406 /* SDWebImageWebPCoder */, 32ABE4E82AA3758400331406 /* SDWebImageSVGCoder */, 32ABE4EA2AA3758400331406 /* SDWebImagePDFCoder */, @@ -702,7 +686,6 @@ ); name = "SDWebImageSwiftUIDemo-watchOS WatchKit Extension"; packageProductDependencies = ( - 32ABE4F82AA375A500331406 /* SDWebImage */, 32ABE4FC2AA375A500331406 /* SDWebImageWebPCoder */, 32ABE4FE2AA375A500331406 /* SDWebImageSVGCoder */, 32ABE5002AA375A500331406 /* SDWebImagePDFCoder */, @@ -2025,10 +2008,6 @@ package = 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */; productName = SDWebImagePDFCoder; }; - 32ABE4F82AA375A500331406 /* SDWebImage */ = { - isa = XCSwiftPackageProductDependency; - productName = SDWebImage; - }; 32ABE4FC2AA375A500331406 /* SDWebImageWebPCoder */ = { isa = XCSwiftPackageProductDependency; package = 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */; @@ -2052,30 +2031,14 @@ isa = XCSwiftPackageProductDependency; productName = SDWebImageSwiftUI; }; - 32B13E822AA368B900BE9B5B /* SDWebImage */ = { - isa = XCSwiftPackageProductDependency; - productName = SDWebImage; - }; 32B13E842AA368C600BE9B5B /* SDWebImageSwiftUI */ = { isa = XCSwiftPackageProductDependency; productName = SDWebImageSwiftUI; }; - 32B13E862AA368C900BE9B5B /* SDWebImage */ = { - isa = XCSwiftPackageProductDependency; - productName = SDWebImage; - }; - 32B13E882AA368CC00BE9B5B /* SDWebImage */ = { - isa = XCSwiftPackageProductDependency; - productName = SDWebImage; - }; 32B13E8E2AA368E100BE9B5B /* SDWebImageSwiftUI */ = { isa = XCSwiftPackageProductDependency; productName = SDWebImageSwiftUI; }; - 32B13E902AA368E300BE9B5B /* SDWebImage */ = { - isa = XCSwiftPackageProductDependency; - productName = SDWebImage; - }; 32D5D1752A445C8F0098BDFC /* SDWebImageSwiftUI */ = { isa = XCSwiftPackageProductDependency; productName = SDWebImageSwiftUI; From 25ffe1ef812e6c5ab32d56e8332594bf0f75c69a Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 30 Nov 2023 15:36:19 +0800 Subject: [PATCH 50/90] Change the CI test into macOS 13 Workaround the test framework issues --- .github/workflows/CI.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 7544522b..6cadb844 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -14,7 +14,7 @@ permissions: jobs: Pods: name: Cocoapods Lint - runs-on: macos-12 + runs-on: macos-13 env: DEVELOPER_DIR: /Applications/Xcode_14.1.app steps: @@ -34,7 +34,7 @@ jobs: Demo: name: Run Demo - runs-on: macos-12 + runs-on: macos-13 env: DEVELOPER_DIR: /Applications/Xcode_14.1.app WORKSPACE_NAME: SDWebImageSwiftUI.xcworkspace @@ -44,11 +44,11 @@ jobs: WATCHSCHEME: SDWebImageSwiftUIDemo-watchOS WatchKit App strategy: matrix: - iosDestination: ["name=iPhone 13 Pro"] - tvOSDestination: ["name=Apple TV"] + iosDestination: ["platform=iOS Simulator,name=iPhone 14 Pro"] + tvOSDestination: ["platform=tvOS Simulator,name=Apple TV"] watchOSDestination: ["platform=watchOS Simulator,name=Apple Watch Series 7 (45mm)"] macOSDestination: ["platform=macOS"] - macCatalystDestination: ["platform=macOS,arch=x86_64,variant=Mac Catalyst"] + macCatalystDestination: ["platform=macOS,variant=Mac Catalyst"] steps: - name: Checkout uses: actions/checkout@v2 @@ -92,7 +92,7 @@ jobs: Test: name: Unit Test - runs-on: macos-12 + runs-on: macos-13 env: DEVELOPER_DIR: /Applications/Xcode_14.1.app WORKSPACE_NAME: SDWebImageSwiftUI.xcworkspace @@ -101,9 +101,9 @@ jobs: TVSCHEME: SDWebImageSwiftUITests tvOS strategy: matrix: - iosDestination: ["platform=iOS Simulator,name=iPhone 13 Pro"] - macOSDestination: ["platform=macOS,arch=x86_64"] - tvOSDestination: ["platform=tvOS Simulator,name=Apple TV 4K"] + iosDestination: ["platform=iOS Simulator,name=iPhone 14 Pro"] + macOSDestination: ["platform=macOS"] + tvOSDestination: ["platform=tvOS Simulator,name=Apple TV"] steps: - name: Checkout uses: actions/checkout@v2 @@ -154,7 +154,7 @@ jobs: Build: name: Build Library - runs-on: macos-12 + runs-on: macos-13 env: DEVELOPER_DIR: /Applications/Xcode_14.1.app PROJECT_NAME: SDWebImageSwiftUI.xcodeproj From 430f68f4a10c77eef4f0b407f8cfe540607b23c2 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 4 Dec 2023 15:33:56 +0800 Subject: [PATCH 51/90] Released v3.0.0-beta.3 version Update the CHANGELOG.md --- CHANGELOG.md | 5 +++++ SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44349541..a64870b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.0.0-beta.3] - 2023-12-04 + +### Changed +- Update the AnimatedImage API to expose the SDAnimatedImageView #285 +- Fix the AnimatedImgae rendering mode about compatible with SDWebImage 5.18+ ## [3.0.0-beta.2] - 2023-10-21 diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index c9612f07..d2810ae8 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '3.0.0-beta.2' + s.version = '3.0.0-beta.3' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index f4b0a0bb..7318b946 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 3.0.0-beta.2 + 3.0.0-beta.3 CFBundleVersion $(CURRENT_PROJECT_VERSION) From 79961a5ac3ea7a3253ff643291ff3c158777029c Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Tue, 19 Dec 2023 22:50:30 +0800 Subject: [PATCH 52/90] Update README.md Fix the documentation link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 100638a5..c9363c84 100644 --- a/README.md +++ b/README.md @@ -326,7 +326,7 @@ For more information, it's really recommended to check our demo, to learn detail ## Documentation -+ [SDWebImageSwiftUI API documentation](https://sdwebimage.github.io/SDWebImageSwiftUI/) ++ [SDWebImageSwiftUI API documentation](https://sdwebimage.github.io/documentation/sdwebimageswiftui/) + [SDWebImage API documentation](https://sdwebimage.github.io/) ## FAQ From 9c6349cf322ac987f30396aed2b7b184cda826b5 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 28 Dec 2023 16:54:39 +0800 Subject: [PATCH 53/90] Update demo to add AVIF test images and AVIF animated images --- .../SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme | 3 +-- Example/SDWebImageSwiftUIDemo/AppDelegate.swift | 2 ++ Example/SDWebImageSwiftUIDemo/ContentView.swift | 2 ++ Podfile | 2 ++ README.md | 4 ++++ 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme index 58b52ed0..65df20bc 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme +++ b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme @@ -53,8 +53,7 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - allowLocationSimulation = "YES" - notificationPayloadFile = "PushNotificationPayload.apns"> + allowLocationSimulation = "YES"> ['libdav1d'] end def all_test_pods diff --git a/README.md b/README.md index c9363c84..e518b35e 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,7 @@ The best place to put these setup code for SwiftUI App, it's the `AppDelegate.sw func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Add WebP/SVG/PDF support SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared) + SDImageCodersManager.shared.addCoder(SDImageAVIFCoder.shared) SDImageCodersManager.shared.addCoder(SDImageSVGCoder.shared) SDImageCodersManager.shared.addCoder(SDImagePDFCoder.shared) @@ -477,6 +478,8 @@ struct MyApp: App { SDWebImageManager.defaultImageLoader = SDImageLoadersManager.shared // WebP support SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared) + // AVIF support + SDImageCodersManager.shared.addCoder(SDImageAVIFCoder.shared) } var body: some Scene { @@ -716,6 +719,7 @@ Which means, this project is one core use case and downstream dependency, which - [SDWebImage](https://github.com/SDWebImage/SDWebImage) - [libwebp](https://github.com/SDWebImage/libwebp-Xcode) +- [libavif](https://github.com/SDWebImage/libavif-Xcode) - [Kingfisher](https://github.com/onevcat/Kingfisher) - [SwiftUIX](https://github.com/SwiftUIX/SwiftUIX) - [Espera](https://github.com/JagCesar/Espera) From 60bcf1e9d57b248b6a6809e285b30716285931d4 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 29 Dec 2023 15:47:02 +0800 Subject: [PATCH 54/90] Fix GitHub CI again :) --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 6cadb844..81160146 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -46,7 +46,7 @@ jobs: matrix: iosDestination: ["platform=iOS Simulator,name=iPhone 14 Pro"] tvOSDestination: ["platform=tvOS Simulator,name=Apple TV"] - watchOSDestination: ["platform=watchOS Simulator,name=Apple Watch Series 7 (45mm)"] + watchOSDestination: ["platform=watchOS Simulator,os=10.0,name=Apple Watch Series 7 (45mm)"] macOSDestination: ["platform=macOS"] macCatalystDestination: ["platform=macOS,variant=Mac Catalyst"] steps: From 3ce301076dc63f01c99e377a56797a975a5a140e Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Tue, 13 Feb 2024 10:26:23 +0800 Subject: [PATCH 55/90] Upgrade to support visionOS on CocoaPods This is needed by SDWebImage Core Example --- README.md | 1 + SDWebImageSwiftUI.podspec | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index c9363c84..a5ab3279 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ All issue reports, feature requests, contributions, and GitHub stars are welcome + macOS 11+ + tvOS 14+ + watchOS 7+ ++ visionOS 1+ ## for SwiftUI 1.0 (iOS 13) diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index d2810ae8..ac36be43 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -25,6 +25,7 @@ It brings all your favorite features from SDWebImage, like async image loading, s.osx.deployment_target = '11.0' s.tvos.deployment_target = '14.0' s.watchos.deployment_target = '7.0' + s.visionos.deployment_target = '1.0' s.source_files = 'SDWebImageSwiftUI/Classes/**/*', 'SDWebImageSwiftUI/Module/*.h' s.pod_target_xcconfig = { From 5d6502aba91ad8dfa171a528eee4963fa3e78ffe Mon Sep 17 00:00:00 2001 From: Arnaud Dorgans Date: Fri, 8 Mar 2024 18:28:38 +0100 Subject: [PATCH 56/90] workaround ratio (cherry picked from commit b7e5780adcf1aa03a7847be170d84820a31425a8) --- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 7e0248fb..2357a8f2 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -600,6 +600,11 @@ extension AnimatedImage { // Aspect Ratio @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { + func setImageLayoutAspectRatio(_ aspectRatio: CGFloat?, contentMode: ContentMode) { + self.imageLayout.aspectRatio = aspectRatio + self.imageLayout.contentMode = contentMode + } + /// Constrains this view's dimensions to the specified aspect ratio. /// - Parameters: /// - aspectRatio: The ratio of width to height to use for the resulting @@ -609,6 +614,7 @@ extension AnimatedImage { /// fill the parent context. /// - Returns: A view that constrains this view's dimensions to /// `aspectRatio`, using `contentMode` as its scaling algorithm. + @ViewBuilder public func aspectRatio(_ aspectRatio: CGFloat? = nil, contentMode: ContentMode) -> some View { // The `SwifUI.View.aspectRatio(_:contentMode:)` says: // If `aspectRatio` is `nil`, the resulting view maintains this view's aspect ratio @@ -618,9 +624,12 @@ extension AnimatedImage { // But 2: there are no way to call a Protocol Extention default implementation in Swift 5.1 // So, we directly call the implementation detail modifier instead // Fired Radar: FB7413534 - self.imageLayout.aspectRatio = aspectRatio - self.imageLayout.contentMode = contentMode - return self.modifier(_AspectRatioLayout(aspectRatio: aspectRatio, contentMode: contentMode)) + let _ = self.setImageLayoutAspectRatio(aspectRatio, contentMode: contentMode) + if let aspectRatio { + self.modifier(_AspectRatioLayout(aspectRatio: aspectRatio, contentMode: contentMode)) + } else { + self + } } /// Constrains this view's dimensions to the aspect ratio of the given size. From e057a3cda6aabfa94eda0f604518f76c5efe8ca1 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 9 Mar 2024 22:44:01 +0800 Subject: [PATCH 57/90] Release 3.0.0 This is the initial release. In the future this SwiftUI repo will merged into SDWebImage Core repo --- .github/workflows/CI.yml | 2 +- CHANGELOG.md | 5 +++++ Package.resolved | 4 ++-- README.md | 27 +++++++++++++++++++++++++-- SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 6 files changed, 35 insertions(+), 7 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 81160146..0f5e54a3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -46,7 +46,7 @@ jobs: matrix: iosDestination: ["platform=iOS Simulator,name=iPhone 14 Pro"] tvOSDestination: ["platform=tvOS Simulator,name=Apple TV"] - watchOSDestination: ["platform=watchOS Simulator,os=10.0,name=Apple Watch Series 7 (45mm)"] + watchOSDestination: ["platform=watchOS Simulator,name=Apple Watch Series 8 (45mm)"] macOSDestination: ["platform=macOS"] macCatalystDestination: ["platform=macOS,variant=Mac Catalyst"] steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index a64870b7..95844a61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.0.0] - 2024-03-09 +- This is the first release for 3.x version. Bump the min deplouyment from SwiftUI 1.0 to 2.0 (means iOS 14/macOS 11/tvOS 14/watchOS 7/visionOS 1) +- Fix AnimatedImage aspectRatio issue when ratio is nil #301 +- Upgrade to support visionOS on CocoaPods #298 + ## [3.0.0-beta.3] - 2023-12-04 ### Changed diff --git a/Package.resolved b/Package.resolved index 81bbc430..09a660d1 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git", "state": { "branch": null, - "revision": "3e48cb68d8e668d146dc59c73fb98cb628616236", - "version": "5.13.2" + "revision": "73b9397cfbd902f606572964055464903b1d84c6", + "version": "5.19.0" } } ] diff --git a/README.md b/README.md index 899dac04..ded65a2c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The framework provide the different View structs, which API match the SwiftUI fr ## Apple VisionOS -From v3.0.0 (beta), SDWebImageSwiftUI can be compiled for visionOS platform. However, due to the lacking package manager support (need tools update), we don't support CocoaPods/SPM yet. +From v3.0.0, SDWebImageSwiftUI can be compiled for visionOS platform. However, due to the lacking package manager support (need tools update), we don't support CocoaPods/SPM yet. You can only use the Xcode's built-in package manager dependency to build on visionOS. @@ -75,7 +75,30 @@ All issue reports, feature requests, contributions, and GitHub stars are welcome iOS 14(macOS 11) introduce the SwiftUI 2.0, which keep the most API compatible, but changes many internal behaviors, which breaks the SDWebImageSwiftUI's function. -From v3.0.0 (Beta), SDWebImageSwiftUI drop iOS 13 support. To use on iOS 13, checkout the latest v2.x version (or using `2.x` branch) instead. +From v3.0.0, SDWebImageSwiftUI drop iOS 13 support. To use on iOS 13, checkout the latest v2.x version (or using `2.x` branch) instead. + +## for future transition + +Since SDWebImage 6.0 will introduce mixed Swift/Objc codebase, this repo will migrate into [SDWebImage Core Repo](https://github.com/SDWebImage/SDWebImage). + +But don't worry, we will use the automatic cross module overlay, whic means, you can use: + +```swift +import SwiftUI +import SDWebImage +``` + +to works like: + +``` +import SwiftUI +import SDWebImage +import SDWebImageSwiftUI // <-- Automatic infer this +``` + +You will automatically link the `SDWebImageSwiftUI`, and this library's naming will still be preserved in SPM target. So the transition is smooth for most of you, I don't want to bump another major version. **The 3.x is the final version for SDWebImageSwiftUI dedicated repo** + +Note: For super advanced user, if you using some custom Swift toolchain, be sure to pass `-Xfrontend -enable-cross-import-overlays` ## Installation diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index ac36be43..f347cfa9 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '3.0.0-beta.3' + s.version = '3.0.0' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index 7318b946..d7939ad4 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 3.0.0-beta.3 + 3.0.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) From 94ae5347ba78b335839adaf8ed7344d5a6c3b8a6 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 9 Mar 2024 23:01:39 +0800 Subject: [PATCH 58/90] Upgrade the github-ci to macOS 14 --- .github/workflows/CI.yml | 113 +++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 52 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0f5e54a3..938cccf7 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -14,18 +14,24 @@ permissions: jobs: Pods: name: Cocoapods Lint - runs-on: macos-13 + runs-on: macos-14 env: - DEVELOPER_DIR: /Applications/Xcode_14.1.app + DEVELOPER_DIR: /Applications/Xcode_15.2.app steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install Cocoapods run: gem install cocoapods --no-document --quiet - name: Install Xcpretty run: gem install xcpretty --no-document --quiet + + - name: Pod Update + run: pod repo update --silent + + - name: Pod Install + run: pod install - name: Run SDWebImageSwiftUI podspec lint run: | @@ -34,24 +40,23 @@ jobs: Demo: name: Run Demo - runs-on: macos-13 + runs-on: macos-14 env: - DEVELOPER_DIR: /Applications/Xcode_14.1.app + DEVELOPER_DIR: /Applications/Xcode_15.2.app WORKSPACE_NAME: SDWebImageSwiftUI.xcworkspace OSXSCHEME: SDWebImageSwiftUIDemo-macOS iOSSCHEME: SDWebImageSwiftUIDemo TVSCHEME: SDWebImageSwiftUIDemo-tvOS WATCHSCHEME: SDWebImageSwiftUIDemo-watchOS WatchKit App - strategy: - matrix: - iosDestination: ["platform=iOS Simulator,name=iPhone 14 Pro"] - tvOSDestination: ["platform=tvOS Simulator,name=Apple TV"] - watchOSDestination: ["platform=watchOS Simulator,name=Apple Watch Series 8 (45mm)"] - macOSDestination: ["platform=macOS"] - macCatalystDestination: ["platform=macOS,variant=Mac Catalyst"] + iosDestination: platform=iOS Simulator,name=iPhone 15 Pro + macOSDestination: platform=macOS,arch=x86_64 + macCatalystDestination: platform=macOS,arch=x86_64,variant=Mac Catalyst + tvOSDestination: platform=tvOS Simulator,name=Apple TV 4K (3rd generation) + watchOSDestination: platform=watchOS Simulator,name=Apple Watch Series 9 (45mm) + visionOSDestination: platform=visionOS Simulator,name=Apple Vision Pro steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Clean DerivedData run: | @@ -73,46 +78,58 @@ jobs: - name: Run demo for OSX run: | set -o pipefail - xcodebuild build -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.OSXSCHEME }}" -destination "${{ matrix.macOSDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c + xcodebuild build -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.OSXSCHEME }}" -destination "${{ env.macOSDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c - name: Run demo for iOS run: | set -o pipefail - xcodebuild build -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.iOSSCHEME }}" -destination "${{ matrix.iosDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c + xcodebuild build -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.iOSSCHEME }}" -destination "${{ env.iosDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c - name: Run demo for TV run: | set -o pipefail - xcodebuild build -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.TVSCHEME }}" -destination "${{ matrix.tvOSDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c + xcodebuild build -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.TVSCHEME }}" -destination "${{ env.tvOSDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c - name: Run demo for Watch run: | set -o pipefail - xcodebuild build -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.WATCHSCHEME }}" -destination "${{ matrix.watchOSDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c + xcodebuild build -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.WATCHSCHEME }}" -destination "${{ env.watchOSDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c Test: name: Unit Test - runs-on: macos-13 + runs-on: macos-14 env: - DEVELOPER_DIR: /Applications/Xcode_14.1.app + DEVELOPER_DIR: /Applications/Xcode_15.2.app WORKSPACE_NAME: SDWebImageSwiftUI.xcworkspace - OSXSCHEME: SDWebImageSwiftUITests macOS - iOSSCHEME: SDWebImageSwiftUITests - TVSCHEME: SDWebImageSwiftUITests tvOS + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + # use matrix to generate jobs for each platform strategy: + fail-fast: false matrix: - iosDestination: ["platform=iOS Simulator,name=iPhone 14 Pro"] - macOSDestination: ["platform=macOS"] - tvOSDestination: ["platform=tvOS Simulator,name=Apple TV"] + platform: [iOS, macOS, tvOS] + include: + - platform: iOS + destination: platform=iOS Simulator,name=iPhone 15 Pro + scheme: SDWebImageSwiftUITests + flag: ios + - platform: macOS + destination: platform=macOS,arch=x86_64 + scheme: SDWebImageSwiftUITests macOS + flag: macos + - platform: tvOS + destination: platform=tvOS Simulator,name=Apple TV 4K (3rd generation) + scheme: SDWebImageSwiftUITests tvOS + flag: tvos + # - platform: visionOS + # destination: platform=visionOS Simulator,name=Apple Vision Pro + # scheme: Vision + # flag: visionos steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 + with: + fetch-depth: 0 - - name: Clean DerivedData - run: | - rm -rf ~/Library/Developer/Xcode/DerivedData/ - mkdir DerivedData - - name: Install Cocoapods run: gem install cocoapods --no-document --quiet @@ -124,39 +141,31 @@ jobs: - name: Pod Install run: pod install - - - name: Test - ${{ matrix.iosDestination }} - run: | - set -o pipefail - xcodebuild test -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.iOSSCHEME }}" -destination "${{ matrix.iosDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c - mv ~/Library/Developer/Xcode/DerivedData/ ./DerivedData/iOS - - - name: Test - ${{ matrix.macOSDestination }} + + - name: Clean DerivedData run: | - set -o pipefail - xcodebuild test -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.OSXSCHEME }}" -destination "${{ matrix.macOSDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO - mv ~/Library/Developer/Xcode/DerivedData/ ./DerivedData/macOS - - - name: Test - ${{ matrix.tvOSDestination }} + rm -rf ~/Library/Developer/Xcode/DerivedData/ + mkdir DerivedData + + - name: Run test run: | set -o pipefail - xcodebuild test -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.TVSCHEME }}" -destination "${{ matrix.tvOSDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c - mv ~/Library/Developer/Xcode/DerivedData/ ./DerivedData/tvOS + xcodebuild build-for-testing -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ matrix.scheme }}" -destination "${{ matrix.destination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c + xcodebuild test-without-building -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ matrix.scheme }}" -destination "${{ matrix.destination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO + mv ~/Library/Developer/Xcode/DerivedData/ "./DerivedData/${{ matrix.platform }}" - name: Code Coverage run: | set -o pipefail export PATH="/usr/local/opt/curl/bin:$PATH" curl --version - bash <(curl -s https://codecov.io/bash) -D './DerivedData/macOS' -J '^SDWebImageSwiftUI$' -c -X gcov -F macos - bash <(curl -s https://codecov.io/bash) -D './DerivedData/iOS' -J '^SDWebImageSwiftUI$' -c -X gcov -F ios - bash <(curl -s https://codecov.io/bash) -D './DerivedData/tvOS' -J '^SDWebImageSwiftUI$' -c -X gcov -F tvos + bash <(curl -s https://codecov.io/bash) -v -D "./DerivedData/${{ matrix.platform }}" -J '^SDWebImageSwiftUI$' -c -X gcov -F "${{ matrix.flag }}" Build: name: Build Library - runs-on: macos-13 + runs-on: macos-14 env: - DEVELOPER_DIR: /Applications/Xcode_14.1.app + DEVELOPER_DIR: /Applications/Xcode_15.2.app PROJECT_NAME: SDWebImageSwiftUI.xcodeproj OSXSCHEME: SDWebImageSwiftUI macOS iOSSCHEME: SDWebImageSwiftUI @@ -164,7 +173,7 @@ jobs: WATCHSCHEME: SDWebImageSwiftUI watchOS steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Build the SwiftPM run: | From 24c18bf4030fcbab56869066d7ccf5d935026ebd Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 9 Mar 2024 23:06:29 +0800 Subject: [PATCH 59/90] Workaround the 32bit target on Xcode 15.2 --- Podfile | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Podfile b/Podfile index 3bfde544..e7e7ce47 100644 --- a/Podfile +++ b/Podfile @@ -59,4 +59,27 @@ target 'SDWebImageSwiftUITests tvOS' do project test_project_path platform :tvos, '14.0' all_test_pods -end \ No newline at end of file +end + + +# Inject macro during SDWebImage Demo and Tests +post_install do |installer_representation| + installer_representation.pods_project.targets.each do |target| + if target.product_name == 'SDWebImage' + target.build_configurations.each do |config| + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) SD_CHECK_CGIMAGE_RETAIN_SOURCE=1' + end + elsif target.product_name == 'SDWebImageSwiftUI' + # Do nothing + else + target.build_configurations.each do |config| + # Override the min deployment target for some test specs to workaround `libarclite.a` missing issue + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '9.0' + config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.11' + config.build_settings['TVOS_DEPLOYMENT_TARGET'] = '9.0' + config.build_settings['WATCHOS_DEPLOYMENT_TARGET'] = '2.0' + config.build_settings['XROS_DEPLOYMENT_TARGET'] = '1.0' + end + end + end +end From fbfd18664c16b0a1b43ce62f2244b4116e99a615 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 9 Mar 2024 23:15:00 +0800 Subject: [PATCH 60/90] Update the github-ci with visionOS --- .github/workflows/CI.yml | 4 +++- Tests/ImageManagerTests.swift | 2 +- Tests/WebImageTests.swift | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 938cccf7..47f6dd7a 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -171,6 +171,7 @@ jobs: iOSSCHEME: SDWebImageSwiftUI TVSCHEME: SDWebImageSwiftUI tvOS WATCHSCHEME: SDWebImageSwiftUI watchOS + VISIONOSSCHEME: SDWebImageSwiftUI visionOS steps: - name: Checkout uses: actions/checkout@v3 @@ -185,7 +186,7 @@ jobs: run: brew install carthage - name: Carthage Update - run: ./carthage.sh update --platform "iOS, tvOS, macOS, watchOS" + run: ./carthage.sh update --platform "iOS, tvOS, macOS, watchOS, visionOS" - name: Build as dynamic frameworks run: | @@ -194,3 +195,4 @@ jobs: xcodebuild build -project "${{ env.PROJECT_NAME }}" -scheme "${{ env.iOSSCHEME }}" -sdk iphoneos -configuration Release | xcpretty -c xcodebuild build -project "${{ env.PROJECT_NAME }}" -scheme "${{ env.TVSCHEME }}" -sdk appletvos -configuration Release | xcpretty -c xcodebuild build -project "${{ env.PROJECT_NAME }}" -scheme "${{ env.WATCHSCHEME }}" -sdk watchos -configuration Release | xcpretty -c + xcodebuild build -project "${{ env.PROJECT_NAME }}" -scheme "${{ env.VISIONOSSCHEME }}" -sdk xros -configuration Release | xcpretty -c diff --git a/Tests/ImageManagerTests.swift b/Tests/ImageManagerTests.swift index ae3269b9..8222f5b2 100644 --- a/Tests/ImageManagerTests.swift +++ b/Tests/ImageManagerTests.swift @@ -17,7 +17,7 @@ class ImageManagerTests: XCTestCase { func testImageManager() throws { let expectation = self.expectation(description: "ImageManager usage with Combine") - let imageUrl = URL(string: "https://via.placeholder.com/500x500.jpg") + let imageUrl = URL(string: "https://placehold.co/500x500.jpg") let imageManager = ImageManager() imageManager.setOnSuccess { image, cacheType, data in XCTAssertNotNil(image) diff --git a/Tests/WebImageTests.swift b/Tests/WebImageTests.swift index bb2ae18c..2270dfa6 100644 --- a/Tests/WebImageTests.swift +++ b/Tests/WebImageTests.swift @@ -137,7 +137,7 @@ class WebImageTests: XCTestCase { func testWebImageOnSuccessWhenCacheMiss() throws { let expectation = self.expectation(description: "WebImage onSuccess when cache miss") - let imageUrl = URL(string: "http://via.placeholder.com/100x100.png") + let imageUrl = URL(string: "https://placehold.co/100x100.png") let cacheKey = SDWebImageManager.shared.cacheKey(for: imageUrl) SDImageCache.shared.removeImageFromMemory(forKey: cacheKey) SDImageCache.shared.removeImageFromDisk(forKey: cacheKey) From 10a19db583cdbe800c5a4ee171b36802f35ad979 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 9 Mar 2024 23:27:14 +0800 Subject: [PATCH 61/90] Update README.md Fill the link to 2.x branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ded65a2c..dd3bf8be 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ All issue reports, feature requests, contributions, and GitHub stars are welcome iOS 14(macOS 11) introduce the SwiftUI 2.0, which keep the most API compatible, but changes many internal behaviors, which breaks the SDWebImageSwiftUI's function. -From v3.0.0, SDWebImageSwiftUI drop iOS 13 support. To use on iOS 13, checkout the latest v2.x version (or using `2.x` branch) instead. +From v3.0.0, SDWebImageSwiftUI drop iOS 13 support. To use on iOS 13, checkout the latest v2.x version (or using [2.x](https://github.com/SDWebImage/SDWebImageSwiftUI/tree/2.x) branch) instead. ## for future transition From 8e445db394bb306a0e871b9cbae244b768df6875 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 9 Mar 2024 23:32:18 +0800 Subject: [PATCH 62/90] Revert the github-ci for visionOS Carthage currently --- .github/workflows/CI.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 47f6dd7a..938cccf7 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -171,7 +171,6 @@ jobs: iOSSCHEME: SDWebImageSwiftUI TVSCHEME: SDWebImageSwiftUI tvOS WATCHSCHEME: SDWebImageSwiftUI watchOS - VISIONOSSCHEME: SDWebImageSwiftUI visionOS steps: - name: Checkout uses: actions/checkout@v3 @@ -186,7 +185,7 @@ jobs: run: brew install carthage - name: Carthage Update - run: ./carthage.sh update --platform "iOS, tvOS, macOS, watchOS, visionOS" + run: ./carthage.sh update --platform "iOS, tvOS, macOS, watchOS" - name: Build as dynamic frameworks run: | @@ -195,4 +194,3 @@ jobs: xcodebuild build -project "${{ env.PROJECT_NAME }}" -scheme "${{ env.iOSSCHEME }}" -sdk iphoneos -configuration Release | xcpretty -c xcodebuild build -project "${{ env.PROJECT_NAME }}" -scheme "${{ env.TVSCHEME }}" -sdk appletvos -configuration Release | xcpretty -c xcodebuild build -project "${{ env.PROJECT_NAME }}" -scheme "${{ env.WATCHSCHEME }}" -sdk watchos -configuration Release | xcpretty -c - xcodebuild build -project "${{ env.PROJECT_NAME }}" -scheme "${{ env.VISIONOSSCHEME }}" -sdk xros -configuration Release | xcpretty -c From 4c4b868b79b2fdd075ba62eb914228afe8e35661 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 18 Mar 2024 17:21:42 +0800 Subject: [PATCH 63/90] Fix the issue for WebImage when url is nil will not cause the reloading --- .../SDWebImageSwiftUIDemo/ContentView.swift | 39 ++++++++++++++++++- SDWebImageSwiftUI/Classes/WebImage.swift | 9 +++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index e27bdb72..743af5b6 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -17,6 +17,43 @@ class UserSettings: ObservableObject { #endif } +// Test Switching nil url +struct ContentView: View { + @State var isOn = false + @State var animated: Bool = false // You can change between WebImage/AnimatedImage + + var url: URL? { + if isOn { + .init(string: "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Google_%22G%22_logo.svg/1024px-Google_%22G%22_logo.svg.png") + } else { + nil + } + } + + var body: some View { + VStack { + Text("\(animated ? "AnimatedImage" : "WebImage")") + Spacer() + if animated { + AnimatedImage(url: url) + .resizable() + .scaledToFit() + .frame(width: 100, height: 100) + } else { + WebImage(url: url) + .resizable() + .scaledToFit() + .frame(width: 100, height: 100) + } + Button("Toggle \(isOn ? "nil" : "valid") URL") { + isOn.toggle() + } + Spacer() + Toggle("Switch", isOn: $animated) + } + } +} + // Test Switching url using @State struct ContentView2: View { @State var imageURLs = [ @@ -63,7 +100,7 @@ struct ContentView2: View { } } -struct ContentView: View { +struct ContentView3: View { @State var imageURLs = [ "http://assets.sbnation.com/assets/2512203/dogflops.gif", "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif", diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 457b6e63..4b0d091c 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -163,6 +163,7 @@ public struct WebImage : View where Content: View { } } else { content((imageManager.error != nil) ? .failure(imageManager.error!) : .empty) + setupPlaceholder() // Load Logic .onPlatformAppear(appear: { self.setupManager() @@ -326,6 +327,14 @@ public struct WebImage : View where Content: View { } } } + + /// Placeholder View Support + func setupPlaceholder() -> some View { + let result = content((imageManager.error != nil) ? .failure(imageManager.error!) : .empty) + // Custom ID to avoid SwiftUI engine cache the status, and does not call `onAppear` when placeholder not changed (See `ContentView.swift/ContentView2` case) + // Because we load the image url in placeholder's `onAppear`, it should be called to sync with state changes :) + return result.id(imageModel.url) + } } // Layout From 6ba07e3c18010f591c7ce045b795a84fa1bc065b Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 18 Mar 2024 17:36:51 +0800 Subject: [PATCH 64/90] Fix the issue for AnimatedImage when url is nil will not cause the reloading --- .../SDWebImageSwiftUIDemo/ContentView.swift | 4 +- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 122 +++++++++++------- 2 files changed, 76 insertions(+), 50 deletions(-) diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index 743af5b6..e31d281a 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -18,7 +18,7 @@ class UserSettings: ObservableObject { } // Test Switching nil url -struct ContentView: View { +struct ContentView3: View { @State var isOn = false @State var animated: Bool = false // You can change between WebImage/AnimatedImage @@ -100,7 +100,7 @@ struct ContentView2: View { } } -struct ContentView3: View { +struct ContentView: View { @State var imageURLs = [ "http://assets.sbnation.com/assets/2512203/dogflops.gif", "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif", diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 2357a8f2..fd2fdf57 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -27,6 +27,13 @@ public final class AnimatedImageCoordinator: NSObject { /// Data Binding Object, only properties in this object can support changes from user with @State and refresh @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedImageModel : ObservableObject { + enum Kind { + case url + case data + case name + case unknown + } + var kind: Kind = .unknown /// URL image @Published var url: URL? @Published var webOptions: SDWebImageOptions = [] @@ -123,6 +130,7 @@ public struct AnimatedImage : PlatformViewRepresentable { /// - Parameter isAnimating: The binding for animation control public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), placeholderImage: PlatformImage? = nil) { let imageModel = AnimatedImageModel() + imageModel.kind = .url imageModel.url = url imageModel.webOptions = options imageModel.webContext = context @@ -138,6 +146,7 @@ public struct AnimatedImage : PlatformViewRepresentable { /// - Parameter isAnimating: The binding for animation control public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), @ViewBuilder placeholder: @escaping () -> T) where T : View { let imageModel = AnimatedImageModel() + imageModel.kind = .url imageModel.url = url imageModel.webOptions = options imageModel.webContext = context @@ -157,6 +166,7 @@ public struct AnimatedImage : PlatformViewRepresentable { /// - Parameter isAnimating: The binding for animation control public init(name: String, bundle: Bundle? = nil, isAnimating: Binding = .constant(true)) { let imageModel = AnimatedImageModel() + imageModel.kind = .name imageModel.name = name imageModel.bundle = bundle self.init(imageModel: imageModel, isAnimating: isAnimating) @@ -168,6 +178,7 @@ public struct AnimatedImage : PlatformViewRepresentable { /// - Parameter isAnimating: The binding for animation control public init(data: Data, scale: CGFloat = 1, isAnimating: Binding = .constant(true)) { let imageModel = AnimatedImageModel() + imageModel.kind = .data imageModel.data = data imageModel.scale = scale self.init(imageModel: imageModel, isAnimating: isAnimating) @@ -275,57 +286,72 @@ public struct AnimatedImage : PlatformViewRepresentable { return view } + private func updateViewForName(_ name: String, view: AnimatedImageViewWrapper, context: Context) { + var image: PlatformImage? + #if os(macOS) + image = SDAnimatedImage(named: name, in: imageModel.bundle) + if image == nil { + // For static image, use NSImage as defaults + let bundle = imageModel.bundle ?? .main + image = bundle.image(forResource: name) + } + #else + image = SDAnimatedImage(named: name, in: imageModel.bundle, compatibleWith: nil) + if image == nil { + // For static image, use UIImage as defaults + image = PlatformImage(named: name, in: imageModel.bundle, compatibleWith: nil) + } + #endif + context.coordinator.imageLoading.imageName = name + view.wrapped.image = image + } + + private func updateViewForData(_ data: Data, view: AnimatedImageViewWrapper, context: Context) { + var image: PlatformImage? = SDAnimatedImage(data: data, scale: imageModel.scale) + if image == nil { + // For static image, use UIImage as defaults + image = PlatformImage.sd_image(with: data, scale: imageModel.scale) + } + context.coordinator.imageLoading.imageData = data + view.wrapped.image = image + } + + private func updateViewForURL(_ url: URL?, view: AnimatedImageViewWrapper, context: Context) { + // Determine if image already been loaded and URL is match + var shouldLoad: Bool + if url != context.coordinator.imageLoading.imageURL { + // Change the URL, need new loading + shouldLoad = true + context.coordinator.imageLoading.imageURL = url + } else { + // Same URL, check if already loaded + if context.coordinator.imageLoading.isLoading { + shouldLoad = false + } else if let image = context.coordinator.imageLoading.image { + shouldLoad = false + view.wrapped.image = image + } else { + shouldLoad = true + } + } + if shouldLoad { + setupIndicator(view, context: context) + loadImage(view, context: context) + } + } + func updateView(_ view: AnimatedImageViewWrapper, context: Context) { // Refresh image, imageModel is the Source of Truth, switch the type // Although we have Source of Truth, we can check the previous value, to avoid re-generate SDAnimatedImage, which is performance-cost. - if let name = imageModel.name, name != context.coordinator.imageLoading.imageName { - var image: PlatformImage? - #if os(macOS) - image = SDAnimatedImage(named: name, in: imageModel.bundle) - if image == nil { - // For static image, use NSImage as defaults - let bundle = imageModel.bundle ?? .main - image = bundle.image(forResource: name) - } - #else - image = SDAnimatedImage(named: name, in: imageModel.bundle, compatibleWith: nil) - if image == nil { - // For static image, use UIImage as defaults - image = PlatformImage(named: name, in: imageModel.bundle, compatibleWith: nil) - } - #endif - context.coordinator.imageLoading.imageName = name - view.wrapped.image = image - } else if let data = imageModel.data, data != context.coordinator.imageLoading.imageData { - var image: PlatformImage? = SDAnimatedImage(data: data, scale: imageModel.scale) - if image == nil { - // For static image, use UIImage as defaults - image = PlatformImage.sd_image(with: data, scale: imageModel.scale) - } - context.coordinator.imageLoading.imageData = data - view.wrapped.image = image - } else if let url = imageModel.url { - // Determine if image already been loaded and URL is match - var shouldLoad: Bool - if url != context.coordinator.imageLoading.imageURL { - // Change the URL, need new loading - shouldLoad = true - context.coordinator.imageLoading.imageURL = url - } else { - // Same URL, check if already loaded - if context.coordinator.imageLoading.isLoading { - shouldLoad = false - } else if let image = context.coordinator.imageLoading.image { - shouldLoad = false - view.wrapped.image = image - } else { - shouldLoad = true - } - } - if shouldLoad { - setupIndicator(view, context: context) - loadImage(view, context: context) - } + let kind = imageModel.kind + if kind == .name, let name = imageModel.name, name != context.coordinator.imageLoading.imageName { + updateViewForName(name, view: view, context: context) + } else if kind == .data, let data = imageModel.data, data != context.coordinator.imageLoading.imageData { + updateViewForData(data, view: view, context: context) + } else if kind == .url { + updateViewForURL(imageModel.url, view: view, context: context) + } else { + fatalError("Unsupported model kind: \(kind)") } #if os(macOS) From 3333a1200c702fa8aad0227018ac6d4c2e927b67 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 18 Mar 2024 17:46:08 +0800 Subject: [PATCH 65/90] Released v3.0.1 version --- CHANGELOG.md | 3 +++ SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95844a61..fba29d20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.0.1] - 2024-03-18 +- Fix the issue for WebImage/AnimatedImage when url is nil will not cause the reloading #304 + ## [3.0.0] - 2024-03-09 - This is the first release for 3.x version. Bump the min deplouyment from SwiftUI 1.0 to 2.0 (means iOS 14/macOS 11/tvOS 14/watchOS 7/visionOS 1) - Fix AnimatedImage aspectRatio issue when ratio is nil #301 diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index f347cfa9..f4f988ed 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '3.0.0' + s.version = '3.0.1' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index d7939ad4..68cfcb0e 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 3.0.0 + 3.0.1 CFBundleVersion $(CURRENT_PROJECT_VERSION) From b550096763c19a68a35819b6b3602767f6db073a Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 27 Mar 2024 16:27:06 +0800 Subject: [PATCH 66/90] Fix the assert then using Data/Name in AnimatedImage Should match the logic as URL, allows to set --- .../SDWebImageSwiftUIDemo/AppDelegate.swift | 4 +++ .../SDWebImageSwiftUIDemo/ContentView.swift | 7 ++++++ SDWebImageSwiftUI/Classes/AnimatedImage.swift | 25 ++++++++++++------- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Example/SDWebImageSwiftUIDemo/AppDelegate.swift b/Example/SDWebImageSwiftUIDemo/AppDelegate.swift index 1b037d42..f08a8c8e 100644 --- a/Example/SDWebImageSwiftUIDemo/AppDelegate.swift +++ b/Example/SDWebImageSwiftUIDemo/AppDelegate.swift @@ -9,7 +9,9 @@ import UIKit import SDWebImage import SDWebImageWebPCoder +#if canImport(SDWebImageAVIFCoder) import SDWebImageAVIFCoder +#endif import SDWebImageSVGCoder import SDWebImagePDFCoder @@ -22,7 +24,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Override point for customization after application launch. // Add WebP/SVG/PDF support SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared) + #if canImport(SDWebImageAVIFCoder) SDImageCodersManager.shared.addCoder(SDImageAVIFCoder.shared) + #endif SDImageCodersManager.shared.addCoder(SDImageSVGCoder.shared) SDImageCodersManager.shared.addCoder(SDImagePDFCoder.shared) // Dynamic check to support vector format for both WebImage/AnimatedImage diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index e31d281a..6e7e74cf 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -34,6 +34,12 @@ struct ContentView3: View { VStack { Text("\(animated ? "AnimatedImage" : "WebImage")") Spacer() + #if os(watchOS) + WebImage(url: url) + .resizable() + .scaledToFit() + .frame(width: 100, height: 100) + #else if animated { AnimatedImage(url: url) .resizable() @@ -45,6 +51,7 @@ struct ContentView3: View { .scaledToFit() .frame(width: 100, height: 100) } + #endif Button("Toggle \(isOn ? "nil" : "valid") URL") { isOn.toggle() } diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index fd2fdf57..6c1b3ba4 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -286,7 +286,10 @@ public struct AnimatedImage : PlatformViewRepresentable { return view } - private func updateViewForName(_ name: String, view: AnimatedImageViewWrapper, context: Context) { + private func updateViewForName(_ name: String?, view: AnimatedImageViewWrapper, context: Context) { + guard let name = name, name != context.coordinator.imageLoading.imageName else { + return + } var image: PlatformImage? #if os(macOS) image = SDAnimatedImage(named: name, in: imageModel.bundle) @@ -306,7 +309,10 @@ public struct AnimatedImage : PlatformViewRepresentable { view.wrapped.image = image } - private func updateViewForData(_ data: Data, view: AnimatedImageViewWrapper, context: Context) { + private func updateViewForData(_ data: Data?, view: AnimatedImageViewWrapper, context: Context) { + guard let data = data, data != context.coordinator.imageLoading.imageData else { + return + } var image: PlatformImage? = SDAnimatedImage(data: data, scale: imageModel.scale) if image == nil { // For static image, use UIImage as defaults @@ -344,14 +350,15 @@ public struct AnimatedImage : PlatformViewRepresentable { // Refresh image, imageModel is the Source of Truth, switch the type // Although we have Source of Truth, we can check the previous value, to avoid re-generate SDAnimatedImage, which is performance-cost. let kind = imageModel.kind - if kind == .name, let name = imageModel.name, name != context.coordinator.imageLoading.imageName { - updateViewForName(name, view: view, context: context) - } else if kind == .data, let data = imageModel.data, data != context.coordinator.imageLoading.imageData { - updateViewForData(data, view: view, context: context) - } else if kind == .url { + switch kind { + case .name: + updateViewForName(imageModel.name, view: view, context: context) + case .data: + updateViewForData(imageModel.data, view: view, context: context) + case .url: updateViewForURL(imageModel.url, view: view, context: context) - } else { - fatalError("Unsupported model kind: \(kind)") + case .unknown: + break // impossible } #if os(macOS) From 22423b017b99ab8ac44e29e6372f571f782af6e4 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 27 Mar 2024 16:58:03 +0800 Subject: [PATCH 67/90] Released v3.0.2 version --- CHANGELOG.md | 3 +++ SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fba29d20..028fe23a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.0.2] - 2024-03-27 +- Fix the assert crash then when using Data/Name in AnimatedImage #309 + ## [3.0.1] - 2024-03-18 - Fix the issue for WebImage/AnimatedImage when url is nil will not cause the reloading #304 diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index f4f988ed..87a78500 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '3.0.1' + s.version = '3.0.2' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index 68cfcb0e..bf7e0944 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 3.0.1 + 3.0.2 CFBundleVersion $(CURRENT_PROJECT_VERSION) From e848d648a10cb964e6950c50294775d8bd70e4ab Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 29 Apr 2024 16:55:55 +0800 Subject: [PATCH 68/90] Added totally empty privacy manifest This because Apple's ITC tool is stupid, since we dependent SDWebImage (which declares the API usage) --- Package.swift | 3 ++- Resources/PrivacyInfo.xcprivacy | 14 ++++++++++++++ SDWebImageSwiftUI.podspec | 3 +++ SDWebImageSwiftUI.xcodeproj/project.pbxproj | 20 ++++++++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 Resources/PrivacyInfo.xcprivacy diff --git a/Package.swift b/Package.swift index 7e53f13c..f55b243f 100644 --- a/Package.swift +++ b/Package.swift @@ -25,7 +25,8 @@ let package = Package( .target( name: "SDWebImageSwiftUI", dependencies: ["SDWebImage"], - path: "SDWebImageSwiftUI/Classes" + path: "SDWebImageSwiftUI/Classes", + resources: [.copy("Resources/PrivacyInfo.xcprivacy")] ), ] ) diff --git a/Resources/PrivacyInfo.xcprivacy b/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..df2b2442 --- /dev/null +++ b/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ + + + + + NSPrivacyTracking + + NSPrivacyAccessedAPITypes + + NSPrivacyCollectedDataTypes + + NSPrivacyTrackingDomains + + + \ No newline at end of file diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 87a78500..0af3d344 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -32,6 +32,9 @@ It brings all your favorite features from SDWebImage, like async image loading, 'SUPPORTS_MACCATALYST' => 'YES', 'DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER' => 'NO', } + s.resource_bundles = { + 'SDWebImageSwiftUI' => ['Resources/PrivacyInfo.xcprivacy'], + } s.weak_frameworks = 'SwiftUI', 'Combine' s.dependency 'SDWebImage', '~> 5.10' diff --git a/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/SDWebImageSwiftUI.xcodeproj/project.pbxproj index d3e5a45d..3c1095c5 100644 --- a/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -66,6 +66,11 @@ 32D26A032446B546005905DA /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; }; 32D26A042446B546005905DA /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; }; 32D26A052446B546005905DA /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; }; + 32FFFE712BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */; }; + 32FFFE722BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */; }; + 32FFFE732BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */; }; + 32FFFE742BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */; }; + 32FFFE752BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -97,6 +102,7 @@ 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImagePlayer.swift; sourceTree = ""; }; 32D26A012446B546005905DA /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = ""; }; 32ED4825242A13030053338E /* ImageManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageManagerTests.swift; sourceTree = ""; }; + 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -199,6 +205,7 @@ 32C43DCE22FD540D00BE87F5 /* SDWebImageSwiftUI */ = { isa = PBXGroup; children = ( + 32FFFE6F2BDF97FB005D0719 /* Resources */, 32C43DDB22FD54C600BE87F5 /* Classes */, ); path = SDWebImageSwiftUI; @@ -233,6 +240,14 @@ name = Frameworks; sourceTree = ""; }; + 32FFFE6F2BDF97FB005D0719 /* Resources */ = { + isa = PBXGroup; + children = ( + 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */, + ); + path = Resources; + sourceTree = SOURCE_ROOT; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -426,6 +441,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 32FFFE752BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -433,6 +449,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 32FFFE712BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -440,6 +457,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 32FFFE722BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -447,6 +465,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 32FFFE732BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -454,6 +473,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 32FFFE742BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 50b5a1ca83873b887dd83a708aa9a7211822f8ea Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 29 Apr 2024 17:20:09 +0800 Subject: [PATCH 69/90] Fix the privacy info path issue --- Package.swift | 3 +- SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI.xcodeproj/project.pbxproj | 30 +++++++++---------- SDWebImageSwiftUI/Assets/.gitkeep | 0 .../Resources}/PrivacyInfo.xcprivacy | 0 5 files changed, 18 insertions(+), 17 deletions(-) delete mode 100644 SDWebImageSwiftUI/Assets/.gitkeep rename {Resources => SDWebImageSwiftUI/Resources}/PrivacyInfo.xcprivacy (100%) diff --git a/Package.swift b/Package.swift index f55b243f..e14a6aac 100644 --- a/Package.swift +++ b/Package.swift @@ -25,7 +25,8 @@ let package = Package( .target( name: "SDWebImageSwiftUI", dependencies: ["SDWebImage"], - path: "SDWebImageSwiftUI/Classes", + path: "SDWebImageSwiftUI", + sources: ["Classes"], resources: [.copy("Resources/PrivacyInfo.xcprivacy")] ), ] diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 0af3d344..0983512d 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -33,7 +33,7 @@ It brings all your favorite features from SDWebImage, like async image loading, 'DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER' => 'NO', } s.resource_bundles = { - 'SDWebImageSwiftUI' => ['Resources/PrivacyInfo.xcprivacy'], + 'SDWebImageSwiftUI' => ['SDWebImageSwiftUI/Resources/PrivacyInfo.xcprivacy'], } s.weak_frameworks = 'SwiftUI', 'Combine' diff --git a/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/SDWebImageSwiftUI.xcodeproj/project.pbxproj index 3c1095c5..4561eb49 100644 --- a/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -66,11 +66,11 @@ 32D26A032446B546005905DA /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; }; 32D26A042446B546005905DA /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; }; 32D26A052446B546005905DA /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; }; - 32FFFE712BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */; }; - 32FFFE722BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */; }; - 32FFFE732BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */; }; - 32FFFE742BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */; }; - 32FFFE752BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */; }; + 32FFFE782BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */; }; + 32FFFE792BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */; }; + 32FFFE7A2BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */; }; + 32FFFE7B2BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */; }; + 32FFFE7C2BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -102,7 +102,7 @@ 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImagePlayer.swift; sourceTree = ""; }; 32D26A012446B546005905DA /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = ""; }; 32ED4825242A13030053338E /* ImageManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageManagerTests.swift; sourceTree = ""; }; - 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -205,7 +205,7 @@ 32C43DCE22FD540D00BE87F5 /* SDWebImageSwiftUI */ = { isa = PBXGroup; children = ( - 32FFFE6F2BDF97FB005D0719 /* Resources */, + 32FFFE762BDF9CFD005D0719 /* Resources */, 32C43DDB22FD54C600BE87F5 /* Classes */, ); path = SDWebImageSwiftUI; @@ -240,13 +240,13 @@ name = Frameworks; sourceTree = ""; }; - 32FFFE6F2BDF97FB005D0719 /* Resources */ = { + 32FFFE762BDF9CFD005D0719 /* Resources */ = { isa = PBXGroup; children = ( - 32FFFE702BDF97FB005D0719 /* PrivacyInfo.xcprivacy */, + 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */, ); path = Resources; - sourceTree = SOURCE_ROOT; + sourceTree = ""; }; /* End PBXGroup section */ @@ -441,7 +441,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32FFFE752BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */, + 32FFFE7C2BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -449,7 +449,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32FFFE712BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */, + 32FFFE782BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -457,7 +457,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32FFFE722BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */, + 32FFFE792BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -465,7 +465,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32FFFE732BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */, + 32FFFE7A2BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -473,7 +473,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32FFFE742BDF97FB005D0719 /* PrivacyInfo.xcprivacy in Resources */, + 32FFFE7B2BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SDWebImageSwiftUI/Assets/.gitkeep b/SDWebImageSwiftUI/Assets/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Resources/PrivacyInfo.xcprivacy b/SDWebImageSwiftUI/Resources/PrivacyInfo.xcprivacy similarity index 100% rename from Resources/PrivacyInfo.xcprivacy rename to SDWebImageSwiftUI/Resources/PrivacyInfo.xcprivacy From 8b26cb7d0d72189babffbd9d5c389aeb784c7222 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 29 Apr 2024 18:13:46 +0800 Subject: [PATCH 70/90] Released v3.0.3 version --- CHANGELOG.md | 4 ++++ SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 028fe23a..5337d231 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.0.3] - 2024-04-29 +- Added totally empty privacy manifest #315 +- People who facing the issue because of Privacy Manifest declaration during ITC validation can try this version + ## [3.0.2] - 2024-03-27 - Fix the assert crash then when using Data/Name in AnimatedImage #309 diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 0983512d..c80c5e0c 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '3.0.2' + s.version = '3.0.3' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index bf7e0944..8b0ce867 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 3.0.2 + 3.0.3 CFBundleVersion $(CURRENT_PROJECT_VERSION) From 5f8a8acf092b3616ee4460df991d06560b9721f5 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 29 Apr 2024 18:19:45 +0800 Subject: [PATCH 71/90] Trying to move the initial state setup before `onAppear` to fix the watchOS switching url or any other state issue This maybe a behavior changes, need testing --- SDWebImageSwiftUI.xcodeproj/project.pbxproj | 12 --- SDWebImageSwiftUI/Classes/ImageManager.swift | 50 +++++++--- .../Classes/Indicator/Indicator.swift | 16 +++- .../Classes/SwiftUICompatibility.swift | 92 ------------------- SDWebImageSwiftUI/Classes/WebImage.swift | 24 +++-- 5 files changed, 68 insertions(+), 126 deletions(-) delete mode 100644 SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift diff --git a/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/SDWebImageSwiftUI.xcodeproj/project.pbxproj index 4561eb49..61bba7e6 100644 --- a/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 3243AFE62AA37EFF0049A43B /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */; }; 3243AFE72AA37EFF0049A43B /* WebImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43DDE22FD54C600BE87F5 /* WebImage.swift */; }; 3243AFE82AA37EFF0049A43B /* ImagePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */; }; 3243AFE92AA37EFF0049A43B /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; }; @@ -26,10 +25,6 @@ 326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; }; 326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; }; 329885EE2AA37FCB0071F2BA /* SDWebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 329885ED2AA37FCB0071F2BA /* SDWebImage.framework */; }; - 32B79C9528DB40430088C432 /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */; }; - 32B79C9628DB40430088C432 /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */; }; - 32B79C9728DB40430088C432 /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */; }; - 32B79C9828DB40430088C432 /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */; }; 32B933E523659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; 32B933E623659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; 32B933E723659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; @@ -83,7 +78,6 @@ 326B84812363350C0011BDFB /* Indicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Indicator.swift; sourceTree = ""; }; 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewWrapper.swift; sourceTree = ""; }; 329885ED2AA37FCB0071F2BA /* SDWebImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDWebImage.framework; path = Carthage/Build/visionOS/SDWebImage.framework; sourceTree = ""; }; - 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUICompatibility.swift; sourceTree = ""; }; 32B933E423659A1900BB7CAD /* Transition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transition.swift; sourceTree = ""; }; 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 = ""; }; @@ -221,7 +215,6 @@ 32C43DDE22FD54C600BE87F5 /* WebImage.swift */, 32C43DDF22FD54C600BE87F5 /* AnimatedImage.swift */, 32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */, - 32B79C9428DB40430088C432 /* SwiftUICompatibility.swift */, 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */, 32D26A012446B546005905DA /* Image.swift */, ); @@ -491,7 +484,6 @@ 3243AFEB2AA37EFF0049A43B /* AnimatedImage.swift in Sources */, 3243AFE82AA37EFF0049A43B /* ImagePlayer.swift in Sources */, 3243AFED2AA37EFF0049A43B /* SDWebImageSwiftUI.swift in Sources */, - 3243AFE62AA37EFF0049A43B /* SwiftUICompatibility.swift in Sources */, 3243AFEE2AA37F010049A43B /* Indicator.swift in Sources */, 3243AFEA2AA37EFF0049A43B /* Image.swift in Sources */, ); @@ -507,7 +499,6 @@ 326B84822363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3222FD5DE100BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480A23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, - 32B79C9528DB40430088C432 /* SwiftUICompatibility.swift in Sources */, 32C43E1622FD583700BE87F5 /* ImageManager.swift in Sources */, 32C43E1822FD583700BE87F5 /* AnimatedImage.swift in Sources */, 32D26A022446B546005905DA /* Image.swift in Sources */, @@ -524,7 +515,6 @@ 326B84832363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3322FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, - 32B79C9628DB40430088C432 /* SwiftUICompatibility.swift in Sources */, 32C43E1922FD583700BE87F5 /* ImageManager.swift in Sources */, 32C43E1B22FD583700BE87F5 /* AnimatedImage.swift in Sources */, 32D26A032446B546005905DA /* Image.swift in Sources */, @@ -541,7 +531,6 @@ 326B84842363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3422FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, - 32B79C9728DB40430088C432 /* SwiftUICompatibility.swift in Sources */, 32C43E1C22FD583800BE87F5 /* ImageManager.swift in Sources */, 32C43E1E22FD583800BE87F5 /* AnimatedImage.swift in Sources */, 32D26A042446B546005905DA /* Image.swift in Sources */, @@ -558,7 +547,6 @@ 326B84852363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3522FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, - 32B79C9828DB40430088C432 /* SwiftUICompatibility.swift in Sources */, 32C43E1F22FD583800BE87F5 /* ImageManager.swift in Sources */, 32C43E2122FD583800BE87F5 /* AnimatedImage.swift in Sources */, 32D26A052446B546005905DA /* Image.swift in Sources */, diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index 008b0a39..dd2bb8c1 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -15,17 +15,47 @@ import SDWebImage @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public final class ImageManager : ObservableObject { /// loaded image, note when progressive loading, this will published multiple times with different partial image - @Published public var image: PlatformImage? + public var image: PlatformImage? { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } /// loaded image data, may be nil if hit from memory cache. This will only published once even on incremental image loading - @Published public var imageData: Data? + public var imageData: Data? { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } /// loaded image cache type, .none means from network - @Published public var cacheType: SDImageCacheType = .none + public var cacheType: SDImageCacheType = .none { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } /// loading error, you can grab the error code and reason listed in `SDWebImageErrorDomain`, to provide a user interface about the error reason - @Published public var error: Error? + public var error: Error? { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } /// true means during incremental loading - @Published public var isIncremental: Bool = false + public var isIncremental: Bool = false { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } /// A observed object to pass through the image manager loading status to indicator - @Published public var indicatorStatus = IndicatorStatus() + public var indicatorStatus = IndicatorStatus() weak var currentOperation: SDWebImageOperation? = nil @@ -51,8 +81,8 @@ public final class ImageManager : ObservableObject { return } currentURL = url - indicatorStatus.isLoading = true - indicatorStatus.progress = 0 + self.indicatorStatus.isLoading = true + self.indicatorStatus.progress = 0 currentOperation = manager.loadImage(with: url, options: options, context: context, progress: { [weak self] (receivedSize, expectedSize, _) in guard let self = self else { return @@ -63,9 +93,7 @@ public final class ImageManager : ObservableObject { } else { progress = 0 } - DispatchQueue.main.async { - self.indicatorStatus.progress = progress - } + self.indicatorStatus.progress = progress self.progressBlock?(receivedSize, expectedSize) }) { [weak self] (image, data, error, cacheType, finished, _) in guard let self = self else { diff --git a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift index 26f0162a..739212e1 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift @@ -28,9 +28,21 @@ public struct Indicator where T : View { @available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) public class IndicatorStatus : ObservableObject { /// whether indicator is loading or not - @Published var isLoading: Bool = false + var isLoading: Bool = false { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } /// indicator progress, should only be used for indicator binding, value between [0.0, 1.0] - @Published var progress: Double = 0 + var progress: Double = 0 { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } } /// A implementation detail View Modifier with indicator diff --git a/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift b/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift deleted file mode 100644 index cb784465..00000000 --- a/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) DreamPiggy - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import Foundation -import SwiftUI - -#if !os(watchOS) - -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -struct PlatformAppear: PlatformViewRepresentable { - let appearAction: () -> Void - let disappearAction: () -> Void - - #if os(iOS) || os(tvOS) || os(visionOS) - func makeUIView(context: Context) -> some UIView { - let view = PlatformAppearView() - view.appearAction = appearAction - view.disappearAction = disappearAction - return view - } - - func updateUIView(_ uiView: UIViewType, context: Context) {} - #endif - #if os(macOS) - func makeNSView(context: Context) -> some NSView { - let view = PlatformAppearView() - view.appearAction = appearAction - view.disappearAction = disappearAction - return view - } - - func updateNSView(_ nsView: NSViewType, context: Context) {} - #endif -} - -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -class PlatformAppearView: PlatformView { - var appearAction: () -> Void = {} - var disappearAction: () -> Void = {} - - #if os(iOS) || os(tvOS) - override func willMove(toWindow newWindow: UIWindow?) { - if newWindow != nil { - DispatchQueue.main.async { - self.appearAction() - } - } else { - DispatchQueue.main.async { - self.disappearAction() - } - } - } - #endif - - #if os(macOS) - override func viewWillMove(toWindow newWindow: NSWindow?) { - if newWindow != nil { - DispatchQueue.main.async { - self.appearAction() - } - } else { - DispatchQueue.main.async { - self.disappearAction() - } - } - } - #endif -} - -#endif - -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -extension View { - /// Used UIKit/AppKit behavior to detect the SwiftUI view's visibility. - /// This hack is because of SwiftUI 1.0/2.0 buggy behavior. The built-in `onAppear` and `onDisappear` is so massive on some cases. Where UIKit/AppKit is solid. - /// - Parameters: - /// - appear: The action when view appears - /// - disappear: The action when view disappears - /// - Returns: Some view - func onPlatformAppear(appear: @escaping () -> Void = {}, disappear: @escaping () -> Void = {}) -> some View { - #if os(iOS) || os(tvOS) || os(macOS) - return self.background(PlatformAppear(appearAction: appear, disappearAction: disappear)) - #else - return self.onAppear(perform: appear).onDisappear(perform: disappear) - #endif - } -} diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 4b0d091c..19b47798 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -163,26 +163,22 @@ public struct WebImage : View where Content: View { } } else { content((imageManager.error != nil) ? .failure(imageManager.error!) : .empty) - setupPlaceholder() + setupInitialState() // Load Logic - .onPlatformAppear(appear: { - self.setupManager() - if (self.imageManager.error == nil) { - // Load remote image when first appear - self.imageManager.load(url: imageModel.url, options: imageModel.options, context: imageModel.context) - } + .onAppear { guard self.imageConfiguration.retryOnAppear else { return } // When using prorgessive loading, the new partial image will cause onAppear. Filter this case if self.imageManager.error != nil && !self.imageManager.isIncremental { self.imageManager.load(url: imageModel.url, options: imageModel.options, context: imageModel.context) } - }, disappear: { + } + .onDisappear { guard self.imageConfiguration.cancelOnDisappear else { return } // When using prorgessive loading, the previous partial image will cause onDisappear. Filter this case if self.imageManager.error != nil && !self.imageManager.isIncremental { self.imageManager.cancel() } - }) + } } } } @@ -328,6 +324,16 @@ public struct WebImage : View where Content: View { } } + /// Initial state management (update when imageModel.url changed) + func setupInitialState() -> some View { + self.setupManager() + if (self.imageManager.error == nil) { + // Load remote image when first appear + self.imageManager.load(url: imageModel.url, options: imageModel.options, context: imageModel.context) + } + return setupPlaceholder() + } + /// Placeholder View Support func setupPlaceholder() -> some View { let result = content((imageManager.error != nil) ? .failure(imageManager.error!) : .empty) From b7af5e6bd9c2987e41730400d1baad13d74a141a Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 29 Apr 2024 18:31:06 +0800 Subject: [PATCH 72/90] Released v3.0.4 version --- CHANGELOG.md | 4 ++++ SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5337d231..51271238 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.0.4] - 2024-04-30 +- Trying to move the initial state setup before onAppear to fix the watchOS switching url or any other state issue #316 +- This solve a issue in history when sometimes SwiftUI does not trigger the `onAppear` and cause state error, like #312 #314 + ## [3.0.3] - 2024-04-29 - Added totally empty privacy manifest #315 - People who facing the issue because of Privacy Manifest declaration during ITC validation can try this version diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index c80c5e0c..158dac83 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '3.0.3' + s.version = '3.0.4' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index 8b0ce867..4f3308f9 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 3.0.3 + 3.0.4 CFBundleVersion $(CURRENT_PROJECT_VERSION) From 075405a3e83cc94d2b71faa5daf5122166521e08 Mon Sep 17 00:00:00 2001 From: woxtu Date: Wed, 12 Jun 2024 20:05:15 +0900 Subject: [PATCH 73/90] Update version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dd3bf8be..516eb3b8 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ For downstream framework author, you should create a `Package.swift` file into y ```swift let package = Package( dependencies: [ - .package(url: "https://github.com/SDWebImage/SDWebImageSwiftUI.git", from: "2.0.0") + .package(url: "https://github.com/SDWebImage/SDWebImageSwiftUI.git", from: "3.0.0") ], ) ``` From 522e8bcbdf0e393fb2baa334574cbd1caceefc24 Mon Sep 17 00:00:00 2001 From: woxtu Date: Sat, 15 Jun 2024 18:20:01 +0900 Subject: [PATCH 74/90] Update platform names --- README.md | 2 +- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 28 +++++++++---------- SDWebImageSwiftUI/Classes/Image.swift | 8 +++--- SDWebImageSwiftUI/Classes/ImageManager.swift | 4 +-- SDWebImageSwiftUI/Classes/ImagePlayer.swift | 2 +- .../Classes/ImageViewWrapper.swift | 4 +-- .../Classes/Indicator/Indicator.swift | 10 +++---- .../Classes/SDWebImageSwiftUI.swift | 22 +++++++-------- .../Classes/Transition/Transition.swift | 2 +- SDWebImageSwiftUI/Classes/WebImage.swift | 20 ++++++------- 10 files changed, 51 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index dd3bf8be..8a487e20 100644 --- a/README.md +++ b/README.md @@ -655,7 +655,7 @@ class ViewController: UIViewController { } // ContentView.swift -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) struct ContentView : View { var body: some View { Group { diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 6c1b3ba4..3070b0ab 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -12,7 +12,7 @@ import SDWebImage #if !os(watchOS) /// A coordinator object used for `AnimatedImage`native view bridge for UIKit/AppKit. -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public final class AnimatedImageCoordinator: NSObject { /// Any user-provided object for actual coordinator, such as delegate method, taget-action @@ -25,7 +25,7 @@ public final class AnimatedImageCoordinator: NSObject { } /// Data Binding Object, only properties in this object can support changes from user with @State and refresh -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedImageModel : ObservableObject { enum Kind { case url @@ -53,7 +53,7 @@ final class AnimatedImageModel : ObservableObject { } /// Loading Binding Object, only properties in this object can support changes from user with @State and refresh -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedLoadingModel : ObservableObject { @Published var image: PlatformImage? // loaded image, note when progressive loading, this will published multiple times with different partial image @Published var isLoading: Bool = false // whether network is loading or cache is querying, should only be used for indicator binding @@ -66,7 +66,7 @@ final class AnimatedLoadingModel : ObservableObject { } /// Completion Handler Binding Object, supports dynamic @State changes -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedImageHandler: ObservableObject { // Completion Handler @Published var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)? @@ -78,7 +78,7 @@ final class AnimatedImageHandler: ObservableObject { } /// Layout Binding Object, supports dynamic @State changes -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedImageLayout : ObservableObject { var contentMode: ContentMode? var aspectRatio: CGFloat? @@ -90,7 +90,7 @@ final class AnimatedImageLayout : ObservableObject { } /// Configuration Binding Object, supports dynamic @State changes -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedImageConfiguration: ObservableObject { var incrementalLoad: Bool? var maxBufferSize: UInt? @@ -106,7 +106,7 @@ final class AnimatedImageConfiguration: ObservableObject { } /// A Image View type to load image from url, data or bundle. Supports animated and static image format. -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public struct AnimatedImage : PlatformViewRepresentable { @ObservedObject var imageModel: AnimatedImageModel @ObservedObject var imageHandler = AnimatedImageHandler() @@ -591,7 +591,7 @@ public struct AnimatedImage : PlatformViewRepresentable { } // Layout -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Configurate this view's image with the specified cap insets and options. @@ -631,7 +631,7 @@ extension AnimatedImage { } // Aspect Ratio -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { func setImageLayoutAspectRatio(_ aspectRatio: CGFloat?, contentMode: ContentMode) { self.imageLayout.aspectRatio = aspectRatio @@ -693,7 +693,7 @@ extension AnimatedImage { } // AnimatedImage Modifier -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Total loop count for animated image rendering. Defaults to nil. @@ -770,7 +770,7 @@ extension AnimatedImage { } // Completion Handler -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Provide the action when image load fails. @@ -802,7 +802,7 @@ extension AnimatedImage { } // View Coordinator Handler -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Provide the action when view representable create the native view. @@ -839,7 +839,7 @@ extension SDWebImageIndicator where Self == SDWebImageProgressIndicator { } // Web Image convenience, based on UIKit/AppKit API -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Associate a indicator when loading image with url @@ -860,7 +860,7 @@ extension AnimatedImage { } #if DEBUG -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) struct AnimatedImage_Previews : PreviewProvider { static var previews: some View { Group { diff --git a/SDWebImageSwiftUI/Classes/Image.swift b/SDWebImageSwiftUI/Classes/Image.swift index 2d8400ed..8978df72 100644 --- a/SDWebImageSwiftUI/Classes/Image.swift +++ b/SDWebImageSwiftUI/Classes/Image.swift @@ -9,7 +9,7 @@ import Foundation import SwiftUI -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension Image { @inlinable init(platformImage: PlatformImage) { #if os(macOS) @@ -20,13 +20,13 @@ extension Image { } } -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension PlatformImage { static var empty = PlatformImage() } #if !os(macOS) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension PlatformImage.Orientation { @inlinable var toSwiftUI: Image.Orientation { switch self { @@ -52,7 +52,7 @@ extension PlatformImage.Orientation { } } -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension Image.Orientation { @inlinable var toPlatform: PlatformImage.Orientation { switch self { diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index dd2bb8c1..f42eccb7 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -12,7 +12,7 @@ import SDWebImage /// A Image observable object for handle image load process. This drive the Source of Truth for image loading status. /// You can use `@ObservedObject` to associate each instance of manager to your View type, which update your view's body from SwiftUI framework when image was loaded. -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public final class ImageManager : ObservableObject { /// loaded image, note when progressive loading, this will published multiple times with different partial image public var image: PlatformImage? { @@ -136,7 +136,7 @@ public final class ImageManager : ObservableObject { } // Completion Handler -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension ImageManager { /// Provide the action when image load fails. /// - Parameters: diff --git a/SDWebImageSwiftUI/Classes/ImagePlayer.swift b/SDWebImageSwiftUI/Classes/ImagePlayer.swift index 8e5820c8..1548744f 100644 --- a/SDWebImageSwiftUI/Classes/ImagePlayer.swift +++ b/SDWebImageSwiftUI/Classes/ImagePlayer.swift @@ -11,7 +11,7 @@ import Combine import SDWebImage /// A Image observable object for handle aniamted image playback. This is used to avoid `@State` update may capture the View struct type and cause memory leak. -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public final class ImagePlayer : ObservableObject { var player: SDAnimatedImagePlayer? diff --git a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift index b0d6ac35..17c5a383 100644 --- a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift +++ b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift @@ -12,7 +12,7 @@ import SDWebImage #if !os(watchOS) /// Use wrapper to solve tne `UIImageView`/`NSImageView` frame size become image size issue (SwiftUI's Bug) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public class AnimatedImageViewWrapper : PlatformView { /// The wrapped actual image view, using SDWebImage's aniamted image view public var wrapped = SDAnimatedImageView() @@ -67,7 +67,7 @@ public class AnimatedImageViewWrapper : PlatformView { } } -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 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. diff --git a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift index 739212e1..bf6cc6b3 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift @@ -10,7 +10,7 @@ import SwiftUI import Combine /// A type to build the indicator -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public struct Indicator where T : View { var content: (Binding, Binding) -> T @@ -25,7 +25,7 @@ public struct Indicator where T : View { } /// A observable model to report indicator loading status -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public class IndicatorStatus : ObservableObject { /// whether indicator is loading or not var isLoading: Bool = false { @@ -48,7 +48,7 @@ public class IndicatorStatus : ObservableObject { /// A implementation detail View Modifier with indicator /// SwiftUI View Modifier construced by using a internal View type which modify the `body` /// It use type system to represent the view hierarchy, and Swift `some View` syntax to hide the type detail for users -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public struct IndicatorViewModifier : ViewModifier where T : View { /// The loading status @@ -72,7 +72,7 @@ public struct IndicatorViewModifier : ViewModifier where T : View { } } -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension Indicator where T == AnyView { /// Activity Indicator public static var activity: Indicator { @@ -90,7 +90,7 @@ extension Indicator where T == AnyView { } } -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension Indicator where T == AnyView { /// Progress Indicator public static var progress: Indicator { diff --git a/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift b/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift index a16d356a..adc40690 100644 --- a/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift +++ b/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift @@ -11,53 +11,53 @@ import SwiftUI @_exported import SDWebImage // Automatically import SDWebImage #if os(macOS) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformImage = NSImage #else -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformImage = UIImage #endif #if os(macOS) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformView = NSView #endif #if os(iOS) || os(tvOS) || os(visionOS) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformView = UIView #endif #if os(watchOS) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformView = WKInterfaceObject #endif #if os(macOS) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformViewRepresentable = NSViewRepresentable #endif #if os(iOS) || os(tvOS) || os(visionOS) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformViewRepresentable = UIViewRepresentable #endif #if os(watchOS) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformViewRepresentable = WKInterfaceObjectRepresentable #endif #if os(macOS) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension NSViewRepresentable { typealias PlatformViewType = NSViewType } #endif #if os(iOS) || os(tvOS) || os(visionOS) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension UIViewRepresentable { typealias PlatformViewType = UIViewType } #endif #if os(watchOS) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension WKInterfaceObjectRepresentable { typealias PlatformViewType = WKInterfaceObjectType } diff --git a/SDWebImageSwiftUI/Classes/Transition/Transition.swift b/SDWebImageSwiftUI/Classes/Transition/Transition.swift index e42503c5..fa3ca48c 100644 --- a/SDWebImageSwiftUI/Classes/Transition/Transition.swift +++ b/SDWebImageSwiftUI/Classes/Transition/Transition.swift @@ -8,7 +8,7 @@ import SwiftUI -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnyTransition { /// Fade-in transition diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 19b47798..cf1080cb 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -47,7 +47,7 @@ public enum WebImagePhase { } /// Data Binding Object, only properties in this object can support changes from user with @State and refresh -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) final class WebImageModel : ObservableObject { /// URL image @Published var url: URL? @@ -56,7 +56,7 @@ final class WebImageModel : ObservableObject { } /// Completion Handler Binding Object, supports dynamic @State changes -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) final class WebImageHandler: ObservableObject { // Completion Handler @Published var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)? @@ -65,7 +65,7 @@ final class WebImageHandler: ObservableObject { } /// Configuration Binding Object, supports dynamic @State changes -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) final class WebImageConfiguration: ObservableObject { var retryOnAppear: Bool = true var cancelOnDisappear: Bool = true @@ -79,7 +79,7 @@ final class WebImageConfiguration: ObservableObject { } /// A Image View type to load image from url. Supports static/animated image format. -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public struct WebImage : View where Content: View { var transaction: Transaction @@ -344,7 +344,7 @@ public struct WebImage : View where Content: View { } // Layout -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { func configure(_ block: @escaping (Image) -> Image) -> WebImage { var result = self @@ -382,7 +382,7 @@ extension WebImage { } // Completion Handler -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { /// Provide the action when image load fails. @@ -414,7 +414,7 @@ extension WebImage { } // WebImage Modifier -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { /// Control the behavior to retry the failed loading when view become appears again /// - Parameter flag: Whether or not to retry the failed loading @@ -432,7 +432,7 @@ extension WebImage { } // Indicator -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { /// Associate a indicator when loading image with url @@ -449,7 +449,7 @@ extension WebImage { } // Animated Image -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { /// Total loop count for animated image rendering. Defaults to nil. @@ -517,7 +517,7 @@ extension WebImage { } #if DEBUG -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) struct WebImage_Previews : PreviewProvider { static var previews: some View { Group { From ecce423f4c32ab1fbf45b154075f04678c667a89 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 25 Jun 2024 23:57:00 +0800 Subject: [PATCH 75/90] Add Image scale support in WebImage init --- SDWebImageSwiftUI/Classes/WebImage.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index cf1080cb..7cc6fbe2 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -108,16 +108,17 @@ public struct WebImage : View where Content: View { /// Create a web image with url, placeholder, custom options and context. Optional can support animated image using Binding. /// - Parameter url: The image url + /// - Parameter scale: The scale to use for the image. The default is 1. Set a different value when loading images designed for higher resolution displays. For example, set a value of 2 for an image that you would name with the @2x suffix if stored in a file on disk. /// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values. /// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. /// - Parameter isAnimating: The binding for animation control. The binding value should be `true` when initialized to setup the correct animated image class. If not, you must provide the `.animatedImageClass` explicitly. When the animation started, this binding can been used to start / stop the animation. - public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true)) where Content == Image { + public init(url: URL?, scale: CGFloat = 1, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true)) where Content == Image { self.init(url: url, options: options, context: context, isAnimating: isAnimating) { phase in phase.image ?? Image(platformImage: .empty) } } - public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), @ViewBuilder content: @escaping (Image) -> I, @ViewBuilder placeholder: @escaping () -> P) where Content == _ConditionalContent, I: View, P: View { + public init(url: URL?, scale: CGFloat = 1, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), @ViewBuilder content: @escaping (Image) -> I, @ViewBuilder placeholder: @escaping () -> P) where Content == _ConditionalContent, I: View, P: View { self.init(url: url, options: options, context: context, isAnimating: isAnimating) { phase in if let i = phase.image { content(i) @@ -127,9 +128,12 @@ public struct WebImage : View where Content: View { } } - public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), transaction: Transaction = Transaction(), @ViewBuilder content: @escaping (WebImagePhase) -> Content) { + public init(url: URL?, scale: CGFloat = 1, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), transaction: Transaction = Transaction(), @ViewBuilder content: @escaping (WebImagePhase) -> Content) { self._isAnimating = isAnimating var context = context ?? [:] + if context[.imageScaleFactor] == nil { + context[.imageScaleFactor] = scale + } // provide animated image class if the initialized `isAnimating` is true, user can still custom the image class if they want if isAnimating.wrappedValue { if context[.animatedImageClass] == nil { From 1edee7f01997a8131889b1d127678007d2763ccd Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 27 Jun 2024 16:37:09 +0800 Subject: [PATCH 76/90] Re-implements the aspectRatio support on AnimatedImage, fix issue like cornerRadius Use the correct way to override invalidateIntrinsicContentSize to keep aspect ratio to UIKit/SwiftUI engine --- .../SDWebImageSwiftUIDemo/ContentView.swift | 11 +++ SDWebImageSwiftUI/Classes/AnimatedImage.swift | 99 ++++--------------- .../Classes/ImageViewWrapper.swift | 18 +++- 3 files changed, 45 insertions(+), 83 deletions(-) diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index 6e7e74cf..e1fc7a57 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -17,6 +17,17 @@ class UserSettings: ObservableObject { #endif } +struct ContentView4: View { + var url = URL(string: "https://github.com/SDWebImage/SDWebImageSwiftUI/assets/97430818/72d27f90-e9d8-48d7-b144-82ada828a027")! + var body: some View { + AnimatedImage(url: url) + .resizable() + .scaledToFit() +// .aspectRatio(nil, contentMode: .fit) + .clipShape(RoundedRectangle(cornerRadius: 50, style: .continuous)) + } +} + // Test Switching nil url struct ContentView3: View { @State var isOn = false diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 3070b0ab..83d0b2c9 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -275,6 +275,9 @@ public struct AnimatedImage : PlatformViewRepresentable { self.imageModel.placeholderView?.isHidden = false self.imageHandler.failureBlock?(error ?? NSError()) } + // Finished loading + configureView(view, context: context) + layoutView(view, context: context) } } @@ -361,20 +364,7 @@ public struct AnimatedImage : PlatformViewRepresentable { break // impossible } - #if os(macOS) - if self.isAnimating != view.wrapped.animates { - view.wrapped.animates = self.isAnimating - } - #else - if self.isAnimating != view.wrapped.isAnimating { - if self.isAnimating { - view.wrapped.startAnimating() - } else { - view.wrapped.stopAnimating() - } - } - #endif - + // Finished loading configureView(view, context: context) layoutView(view, context: context) if let viewUpdateBlock = imageHandler.viewUpdateBlock { @@ -442,9 +432,7 @@ public struct AnimatedImage : PlatformViewRepresentable { #endif // Resizable - if let _ = imageLayout.resizingMode { - view.resizable = true - } + view.resizingMode = imageLayout.resizingMode // Animated Image does not support resizing mode and rendering mode if let image = view.wrapped.image { @@ -587,6 +575,21 @@ public struct AnimatedImage : PlatformViewRepresentable { } else { view.wrapped.playbackMode = .normal } + + // Animation + #if os(macOS) + if self.isAnimating != view.wrapped.animates { + view.wrapped.animates = self.isAnimating + } + #else + if self.isAnimating != view.wrapped.isAnimating { + if self.isAnimating { + view.wrapped.startAnimating() + } else { + view.wrapped.stopAnimating() + } + } + #endif } } @@ -630,68 +633,6 @@ extension AnimatedImage { } } -// Aspect Ratio -@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) -extension AnimatedImage { - func setImageLayoutAspectRatio(_ aspectRatio: CGFloat?, contentMode: ContentMode) { - self.imageLayout.aspectRatio = aspectRatio - self.imageLayout.contentMode = contentMode - } - - /// Constrains this view's dimensions to the specified aspect ratio. - /// - Parameters: - /// - aspectRatio: The ratio of width to height to use for the resulting - /// view. If `aspectRatio` is `nil`, the resulting view maintains this - /// view's aspect ratio. - /// - contentMode: A flag indicating whether this view should fit or - /// fill the parent context. - /// - Returns: A view that constrains this view's dimensions to - /// `aspectRatio`, using `contentMode` as its scaling algorithm. - @ViewBuilder - public func aspectRatio(_ aspectRatio: CGFloat? = nil, contentMode: ContentMode) -> some View { - // The `SwifUI.View.aspectRatio(_:contentMode:)` says: - // If `aspectRatio` is `nil`, the resulting view maintains this view's aspect ratio - // But 1: there are no public API to declare what `this view's aspect ratio` is - // So, if we don't override this method, SwiftUI ignore the content mode on actual ImageView - // To workaround, we want to call the default `SwifUI.View.aspectRatio(_:contentMode:)` method - // But 2: there are no way to call a Protocol Extention default implementation in Swift 5.1 - // So, we directly call the implementation detail modifier instead - // Fired Radar: FB7413534 - let _ = self.setImageLayoutAspectRatio(aspectRatio, contentMode: contentMode) - if let aspectRatio { - self.modifier(_AspectRatioLayout(aspectRatio: aspectRatio, contentMode: contentMode)) - } else { - self - } - } - - /// Constrains this view's dimensions to the aspect ratio of the given size. - /// - Parameters: - /// - aspectRatio: A size specifying the ratio of width to height to use - /// for the resulting view. - /// - contentMode: A flag indicating whether this view should fit or - /// fill the parent context. - /// - Returns: A view that constrains this view's dimensions to - /// `aspectRatio`, using `contentMode` as its scaling algorithm. - public func aspectRatio(_ aspectRatio: CGSize, contentMode: ContentMode) -> some View { - return self.aspectRatio(aspectRatio.width / aspectRatio.height, contentMode: contentMode) - } - - /// Scales this view to fit its parent. - /// - Returns: A view that scales this view to fit its parent, - /// maintaining this view's aspect ratio. - public func scaledToFit() -> some View { - return self.aspectRatio(nil, contentMode: .fit) - } - - /// Scales this view to fill its parent. - /// - Returns: A view that scales this view to fit its parent, - /// maintaining this view's aspect ratio. - public func scaledToFill() -> some View { - return self.aspectRatio(nil, contentMode: .fill) - } -} - // AnimatedImage Modifier @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { diff --git a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift index 17c5a383..cfe81baf 100644 --- a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift +++ b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift @@ -8,6 +8,7 @@ import Foundation import SDWebImage +import SwiftUI #if !os(watchOS) @@ -18,7 +19,7 @@ public class AnimatedImageViewWrapper : PlatformView { public var wrapped = SDAnimatedImageView() var interpolationQuality = CGInterpolationQuality.default var shouldAntialias = false - var resizable = false + var resizingMode: Image.ResizingMode? public override func draw(_ rect: CGRect) { #if os(macOS) @@ -48,11 +49,20 @@ public class AnimatedImageViewWrapper : PlatformView { public override var intrinsicContentSize: CGSize { /// Match the behavior of SwiftUI.Image, only when image is resizable, use the super implementation to calculate size - if resizable { - return super.intrinsicContentSize + let imageSize = wrapped.intrinsicContentSize + if let _ = resizingMode { + /// Keep aspect ratio + let noIntrinsicMetric = AnimatedImageViewWrapper.noIntrinsicMetric + if (imageSize.width > 0 && imageSize.height > 0) { + let ratio = imageSize.width / imageSize.height + let size = CGSize(width: ratio, height: 1) + return size + } else { + return CGSize(width: noIntrinsicMetric, height: noIntrinsicMetric) + } } else { /// Not resizable, always use image size, like SwiftUI.Image - return wrapped.intrinsicContentSize + return imageSize } } From 3340ea4ed46b10e3a15b95ea10532e4ad2ec93b0 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 27 Jun 2024 17:53:07 +0800 Subject: [PATCH 77/90] Fix the compatibility with UIView transition Actually this is not the good design, but at least a workaround --- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 25 ++++++++++++++----- .../Classes/ImageViewWrapper.swift | 20 ++++++++++----- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 83d0b2c9..193c9eb2 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -275,9 +275,8 @@ public struct AnimatedImage : PlatformViewRepresentable { self.imageModel.placeholderView?.isHidden = false self.imageHandler.failureBlock?(error ?? NSError()) } - // Finished loading - configureView(view, context: context) - layoutView(view, context: context) + // Finished loading, async + finishUpdateView(view, context: context, image: image) } } @@ -310,6 +309,8 @@ public struct AnimatedImage : PlatformViewRepresentable { #endif context.coordinator.imageLoading.imageName = name view.wrapped.image = image + // Finished loading, sync + finishUpdateView(view, context: context, image: image) } private func updateViewForData(_ data: Data?, view: AnimatedImageViewWrapper, context: Context) { @@ -323,6 +324,8 @@ public struct AnimatedImage : PlatformViewRepresentable { } context.coordinator.imageLoading.imageData = data view.wrapped.image = image + // Finished loading, sync + finishUpdateView(view, context: context, image: image) } private func updateViewForURL(_ url: URL?, view: AnimatedImageViewWrapper, context: Context) { @@ -347,6 +350,8 @@ public struct AnimatedImage : PlatformViewRepresentable { setupIndicator(view, context: context) loadImage(view, context: context) } + // Finished loading, sync + finishUpdateView(view, context: context, image: view.wrapped.image) } func updateView(_ view: AnimatedImageViewWrapper, context: Context) { @@ -364,9 +369,6 @@ public struct AnimatedImage : PlatformViewRepresentable { break // impossible } - // Finished loading - configureView(view, context: context) - layoutView(view, context: context) if let viewUpdateBlock = imageHandler.viewUpdateBlock { viewUpdateBlock(view.wrapped, context) } @@ -384,6 +386,17 @@ public struct AnimatedImage : PlatformViewRepresentable { } } + func finishUpdateView(_ view: AnimatedImageViewWrapper, context: Context, image: PlatformImage?) { + // Finished loading + if let imageSize = image?.size { + view.imageSize = imageSize + } else { + view.imageSize = nil + } + configureView(view, context: context) + layoutView(view, context: context) + } + func layoutView(_ view: AnimatedImageViewWrapper, context: Context) { // AspectRatio && ContentMode #if os(macOS) diff --git a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift index cfe81baf..e019881f 100644 --- a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift +++ b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift @@ -20,6 +20,7 @@ public class AnimatedImageViewWrapper : PlatformView { var interpolationQuality = CGInterpolationQuality.default var shouldAntialias = false var resizingMode: Image.ResizingMode? + var imageSize: CGSize? public override func draw(_ rect: CGRect) { #if os(macOS) @@ -49,20 +50,27 @@ public class AnimatedImageViewWrapper : PlatformView { public override var intrinsicContentSize: CGSize { /// Match the behavior of SwiftUI.Image, only when image is resizable, use the super implementation to calculate size - let imageSize = wrapped.intrinsicContentSize + var contentSize = wrapped.intrinsicContentSize + /// Sometimes, like during the transaction, the wrapped.image == nil, which cause contentSize invalid + /// Use image size as backup + /// TODO: This mixed use of UIKit/SwiftUI animation will cause visial issue because the intrinsicContentSize during animation may be changed + if let imageSize = imageSize { + if contentSize != imageSize { + contentSize = imageSize + } + } if let _ = resizingMode { /// Keep aspect ratio - let noIntrinsicMetric = AnimatedImageViewWrapper.noIntrinsicMetric - if (imageSize.width > 0 && imageSize.height > 0) { - let ratio = imageSize.width / imageSize.height + if contentSize.width > 0 && contentSize.height > 0 { + let ratio = contentSize.width / contentSize.height let size = CGSize(width: ratio, height: 1) return size } else { - return CGSize(width: noIntrinsicMetric, height: noIntrinsicMetric) + return contentSize } } else { /// Not resizable, always use image size, like SwiftUI.Image - return imageSize + return contentSize } } From c8320d4e20d00455cb6401331cf54616bceb4e4c Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 27 Jun 2024 23:08:56 +0800 Subject: [PATCH 78/90] Revert the wrong changes to fix the unit test --- Example/SDWebImageSwiftUIDemo/ContentView.swift | 2 ++ SDWebImageSwiftUI/Classes/AnimatedImage.swift | 9 +++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index e1fc7a57..4cb0fa54 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -17,6 +17,7 @@ class UserSettings: ObservableObject { #endif } +#if !os(watchOS) struct ContentView4: View { var url = URL(string: "https://github.com/SDWebImage/SDWebImageSwiftUI/assets/97430818/72d27f90-e9d8-48d7-b144-82ada828a027")! var body: some View { @@ -27,6 +28,7 @@ struct ContentView4: View { .clipShape(RoundedRectangle(cornerRadius: 50, style: .continuous)) } } +#endif // Test Switching nil url struct ContentView3: View { diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 193c9eb2..66543720 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -309,8 +309,6 @@ public struct AnimatedImage : PlatformViewRepresentable { #endif context.coordinator.imageLoading.imageName = name view.wrapped.image = image - // Finished loading, sync - finishUpdateView(view, context: context, image: image) } private func updateViewForData(_ data: Data?, view: AnimatedImageViewWrapper, context: Context) { @@ -324,8 +322,6 @@ public struct AnimatedImage : PlatformViewRepresentable { } context.coordinator.imageLoading.imageData = data view.wrapped.image = image - // Finished loading, sync - finishUpdateView(view, context: context, image: image) } private func updateViewForURL(_ url: URL?, view: AnimatedImageViewWrapper, context: Context) { @@ -350,8 +346,6 @@ public struct AnimatedImage : PlatformViewRepresentable { setupIndicator(view, context: context) loadImage(view, context: context) } - // Finished loading, sync - finishUpdateView(view, context: context, image: view.wrapped.image) } func updateView(_ view: AnimatedImageViewWrapper, context: Context) { @@ -369,6 +363,9 @@ public struct AnimatedImage : PlatformViewRepresentable { break // impossible } + // Finished loading, sync + finishUpdateView(view, context: context, image: view.wrapped.image) + if let viewUpdateBlock = imageHandler.viewUpdateBlock { viewUpdateBlock(view.wrapped, context) } From 02b2579a93aaa8ac0146a46f18d9056253c03096 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 27 Jun 2024 23:28:46 +0800 Subject: [PATCH 79/90] Released v3.1.0 version --- CHANGELOG.md | 6 ++++++ SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51271238..5c27bdc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.1.0] - 2024-06-27 +- Re-implements the aspectRatio support on AnimatedImage, fix issue like cornerRadius #324 +- Add Image scale support in WebImage init #323 +- Update platform names in `available` attributes #321 +- - This is source compatible but binary incompatible version + ## [3.0.4] - 2024-04-30 - Trying to move the initial state setup before onAppear to fix the watchOS switching url or any other state issue #316 - This solve a issue in history when sometimes SwiftUI does not trigger the `onAppear` and cause state error, like #312 #314 diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 158dac83..9d71080a 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '3.0.4' + s.version = '3.1.0' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index 4f3308f9..99601856 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 3.0.4 + 3.1.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) From 26f75715c7492841e5373906b1baca1e32a98ff4 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 1 Jul 2024 16:41:02 +0800 Subject: [PATCH 80/90] Fix the WebImage.transaction should use take effect --- SDWebImageSwiftUI/Classes/ImageManager.swift | 27 +++++++++++--------- SDWebImageSwiftUI/Classes/WebImage.swift | 4 +-- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index f42eccb7..eb5ec273 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -60,6 +60,7 @@ public final class ImageManager : ObservableObject { weak var currentOperation: SDWebImageOperation? = nil var currentURL: URL? + var transaction = Transaction() var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)? var failureBlock: ((Error) -> Void)? var progressBlock: ((Int, Int) -> Void)? @@ -106,18 +107,20 @@ public final class ImageManager : ObservableObject { // So previous View struct call `onDisappear` and cancel the currentOperation return } - self.image = image - self.error = error - self.isIncremental = !finished - if finished { - self.imageData = data - self.cacheType = cacheType - self.indicatorStatus.isLoading = false - self.indicatorStatus.progress = 1 - if let image = image { - self.successBlock?(image, data, cacheType) - } else { - self.failureBlock?(error ?? NSError()) + withTransaction(transaction) { + self.image = image + self.error = error + self.isIncremental = !finished + if finished { + self.imageData = data + self.cacheType = cacheType + self.indicatorStatus.isLoading = false + self.indicatorStatus.progress = 1 + if let image = image { + self.successBlock?(image, data, cacheType) + } else { + self.failureBlock?(error ?? NSError()) + } } } } diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 7cc6fbe2..3dab6a3c 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -81,8 +81,6 @@ final class WebImageConfiguration: ObservableObject { /// A Image View type to load image from url. Supports static/animated image format. @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public struct WebImage : View where Content: View { - var transaction: Transaction - var configurations: [(Image) -> Image] = [] var content: (WebImagePhase) -> Content @@ -146,10 +144,10 @@ public struct WebImage : View where Content: View { imageModel.context = context _imageModel = ObservedObject(wrappedValue: imageModel) let imageManager = ImageManager() + imageManager.transaction = transaction _imageManager = StateObject(wrappedValue: imageManager) _indicatorStatus = ObservedObject(wrappedValue: imageManager.indicatorStatus) - self.transaction = transaction self.content = { phase in content(phase) } From d68c13a7f317adba773f302a99522c7f5732a80a Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 1 Jul 2024 17:59:27 +0800 Subject: [PATCH 81/90] Fix the transition visual jump between placeholderImage and final image for AnimatedImage --- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 11 +++------ .../Classes/ImageViewWrapper.swift | 24 ++++++++++--------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index 66543720..4e5a7a75 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -276,7 +276,7 @@ public struct AnimatedImage : PlatformViewRepresentable { self.imageHandler.failureBlock?(error ?? NSError()) } // Finished loading, async - finishUpdateView(view, context: context, image: image) + finishUpdateView(view, context: context) } } @@ -364,7 +364,7 @@ public struct AnimatedImage : PlatformViewRepresentable { } // Finished loading, sync - finishUpdateView(view, context: context, image: view.wrapped.image) + finishUpdateView(view, context: context) if let viewUpdateBlock = imageHandler.viewUpdateBlock { viewUpdateBlock(view.wrapped, context) @@ -383,13 +383,8 @@ public struct AnimatedImage : PlatformViewRepresentable { } } - func finishUpdateView(_ view: AnimatedImageViewWrapper, context: Context, image: PlatformImage?) { + func finishUpdateView(_ view: AnimatedImageViewWrapper, context: Context) { // Finished loading - if let imageSize = image?.size { - view.imageSize = imageSize - } else { - view.imageSize = nil - } configureView(view, context: context) layoutView(view, context: context) } diff --git a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift index e019881f..ff42fdf1 100644 --- a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift +++ b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift @@ -16,11 +16,15 @@ import SwiftUI @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public class AnimatedImageViewWrapper : PlatformView { /// The wrapped actual image view, using SDWebImage's aniamted image view - public var wrapped = SDAnimatedImageView() + @objc dynamic public var wrapped = SDAnimatedImageView() + var observation: NSKeyValueObservation? var interpolationQuality = CGInterpolationQuality.default var shouldAntialias = false var resizingMode: Image.ResizingMode? - var imageSize: CGSize? + + deinit { + observation?.invalidate() + } public override func draw(_ rect: CGRect) { #if os(macOS) @@ -50,15 +54,7 @@ public class AnimatedImageViewWrapper : PlatformView { public override var intrinsicContentSize: CGSize { /// Match the behavior of SwiftUI.Image, only when image is resizable, use the super implementation to calculate size - var contentSize = wrapped.intrinsicContentSize - /// Sometimes, like during the transaction, the wrapped.image == nil, which cause contentSize invalid - /// Use image size as backup - /// TODO: This mixed use of UIKit/SwiftUI animation will cause visial issue because the intrinsicContentSize during animation may be changed - if let imageSize = imageSize { - if contentSize != imageSize { - contentSize = imageSize - } - } + let contentSize = wrapped.intrinsicContentSize if let _ = resizingMode { /// Keep aspect ratio if contentSize.width > 0 && contentSize.height > 0 { @@ -77,11 +73,17 @@ public class AnimatedImageViewWrapper : PlatformView { public override init(frame frameRect: CGRect) { super.init(frame: frameRect) addSubview(wrapped) + observation = observe(\.wrapped.image, options: [.new]) { _, _ in + self.invalidateIntrinsicContentSize() + } } public required init?(coder: NSCoder) { super.init(coder: coder) addSubview(wrapped) + observation = observe(\.wrapped.image, options: [.new]) { _, _ in + self.invalidateIntrinsicContentSize() + } } } From 5d462f7530677ae0c2b9510c26383aa25ba48751 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 1 Jul 2024 19:47:26 +0800 Subject: [PATCH 82/90] Released v3.1.1 version --- CHANGELOG.md | 3 +++ SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c27bdc1..5fa69ec0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.1.1] - 2024-07-01 +- Fix the transition visual jump between placeholderImage and final image for AnimatedImage #326 + ## [3.1.0] - 2024-06-27 - Re-implements the aspectRatio support on AnimatedImage, fix issue like cornerRadius #324 - Add Image scale support in WebImage init #323 diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 9d71080a..96e7cadc 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '3.1.0' + s.version = '3.1.1' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index 99601856..282c0ff3 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 3.1.0 + 3.1.1 CFBundleVersion $(CURRENT_PROJECT_VERSION) From 7efdf228f68073ec9a95a2308378fae82932f10c Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 28 Aug 2024 17:35:43 +0800 Subject: [PATCH 83/90] Allows easy to use WebImage with `isAnimating` default to false and change to true later Since SDAnimatedImage has fallback logic, we can apply this to WebImage by default without dynamic check --- .../SDWebImageSwiftUIDemo/ContentView.swift | 18 ++++++++++++++++++ SDWebImageSwiftUI/Classes/WebImage.swift | 12 ++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index 4cb0fa54..17e43e7e 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -17,6 +17,24 @@ class UserSettings: ObservableObject { #endif } +struct ContentView5: View { + let url: URL = URL(string: "http://assets.sbnation.com/assets/2512203/dogflops.gif")! + + @State private var isAnimating = false + + var body: some View { + ZStack { + WebImage(url: url, isAnimating: $isAnimating) + .pausable(false) + Button { + isAnimating.toggle() + } label: { + Text(isAnimating ? "Stop" : "Start") + } + } + } +} + #if !os(watchOS) struct ContentView4: View { var url = URL(string: "https://github.com/SDWebImage/SDWebImageSwiftUI/assets/97430818/72d27f90-e9d8-48d7-b144-82ada828a027")! diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 3dab6a3c..59fe49a5 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -109,7 +109,7 @@ public struct WebImage : View where Content: View { /// - Parameter scale: The scale to use for the image. The default is 1. Set a different value when loading images designed for higher resolution displays. For example, set a value of 2 for an image that you would name with the @2x suffix if stored in a file on disk. /// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values. /// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. - /// - Parameter isAnimating: The binding for animation control. The binding value should be `true` when initialized to setup the correct animated image class. If not, you must provide the `.animatedImageClass` explicitly. When the animation started, this binding can been used to start / stop the animation. + /// - Parameter isAnimating: The binding for animation control. When the animation started, this binding can been used to start / stop the animation. You can still customize the `.animatedImageClass` context for advanced custom animation. public init(url: URL?, scale: CGFloat = 1, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true)) where Content == Image { self.init(url: url, options: options, context: context, isAnimating: isAnimating) { phase in phase.image ?? Image(platformImage: .empty) @@ -132,11 +132,11 @@ public struct WebImage : View where Content: View { if context[.imageScaleFactor] == nil { context[.imageScaleFactor] = scale } - // provide animated image class if the initialized `isAnimating` is true, user can still custom the image class if they want - if isAnimating.wrappedValue { - if context[.animatedImageClass] == nil { - context[.animatedImageClass] = SDAnimatedImage.self - } + // always provide animated image class to allows dynamic control + // since most cases, SDAnimatedImage should be compatible with UIImage + // user can still custom the image class if they want + if context[.animatedImageClass] == nil { + context[.animatedImageClass] = SDAnimatedImage.self } let imageModel = WebImageModel() imageModel.url = url From 5aa947356f4ea49a0c3b9968564267f6ea5abea7 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 29 Aug 2024 16:35:40 +0800 Subject: [PATCH 84/90] Released v3.1.2 version --- CHANGELOG.md | 4 ++++ SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fa69ec0..0c1a3efa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.1.2] - 2024-08-29 +- Allows easy to use WebImage with isAnimating default to false and change to true later #333 +- Note: This changes WebImage's internal loaded image from `UIImage/NSImage` to `SDAnimatedImage`, which is compatible for `UIImageView/NSImageView` + ## [3.1.1] - 2024-07-01 - Fix the transition visual jump between placeholderImage and final image for AnimatedImage #326 diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 96e7cadc..5f21a283 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '3.1.1' + s.version = '3.1.2' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index 282c0ff3..0eee3074 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 3.1.1 + 3.1.2 CFBundleVersion $(CURRENT_PROJECT_VERSION) From 7ecc2d33f840f3c5577a27850089f96e6c630d27 Mon Sep 17 00:00:00 2001 From: Narong Aunthee Date: Tue, 5 Nov 2024 16:46:37 +0700 Subject: [PATCH 85/90] Fixed old version compiler does not support automatic self capture in Xcode 14.2 and Swift 5.7.2 --- SDWebImageSwiftUI/Classes/ImageManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index eb5ec273..7da6fc9d 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -107,7 +107,7 @@ public final class ImageManager : ObservableObject { // So previous View struct call `onDisappear` and cancel the currentOperation return } - withTransaction(transaction) { + withTransaction(self.transaction) { self.image = image self.error = error self.isIncremental = !finished From 46407f925dee7346cbb617fa4657dda9433f1f15 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 6 Nov 2024 14:27:51 +0800 Subject: [PATCH 86/90] Fix the data race because progress block is called in non-main queue This match the behavior of `progress indicator`, which only update on main queue Note: This is different behavior compared to SDWebIamge on UIKit (progress updated in global queue) --- SDWebImageSwiftUI/Classes/ImageManager.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index 7da6fc9d..045bfa37 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -85,6 +85,7 @@ public final class ImageManager : ObservableObject { self.indicatorStatus.isLoading = true self.indicatorStatus.progress = 0 currentOperation = manager.loadImage(with: url, options: options, context: context, progress: { [weak self] (receivedSize, expectedSize, _) in + // This block may be called in non-main thread guard let self = self else { return } @@ -95,7 +96,11 @@ public final class ImageManager : ObservableObject { progress = 0 } self.indicatorStatus.progress = progress - self.progressBlock?(receivedSize, expectedSize) + if let progressBlock = self.progressBlock { + DispatchQueue.main.async { + progressBlock(receivedSize, expectedSize) + } + } }) { [weak self] (image, data, error, cacheType, finished, _) in guard let self = self else { return From 451c6dfd5ecec2cf626d1d9ca81c2d4a60355172 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 6 Nov 2024 14:46:42 +0800 Subject: [PATCH 87/90] Released v3.1.3 version --- CHANGELOG.md | 4 ++++ SDWebImageSwiftUI.podspec | 2 +- SDWebImageSwiftUI/Module/Info.plist | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c1a3efa..05a05072 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.1.3] - 2024-11-06 +- Fixed old version compiler does not support automatic self capture in Xcode 14.2 and Swift 5.7.2 #340 +- Fix the data race because progress block is called in non-main queue #341 + ## [3.1.2] - 2024-08-29 - Allows easy to use WebImage with isAnimating default to false and change to true later #333 - Note: This changes WebImage's internal loaded image from `UIImage/NSImage` to `SDAnimatedImage`, which is compatible for `UIImageView/NSImageView` diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 5f21a283..2c26725e 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '3.1.2' + s.version = '3.1.3' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index 0eee3074..df2e4bee 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 3.1.2 + 3.1.3 CFBundleVersion $(CURRENT_PROJECT_VERSION) From 765aea04e0b94442578a7932f0edbed2b6f748af Mon Sep 17 00:00:00 2001 From: wuqi Date: Thu, 22 May 2025 17:26:45 +0800 Subject: [PATCH 88/90] fix: memoryleak --- SDWebImageSwiftUI/Classes/ImageViewWrapper.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift index ff42fdf1..49ac173b 100644 --- a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift +++ b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift @@ -73,7 +73,10 @@ public class AnimatedImageViewWrapper : PlatformView { public override init(frame frameRect: CGRect) { super.init(frame: frameRect) addSubview(wrapped) - observation = observe(\.wrapped.image, options: [.new]) { _, _ in + observation = observe(\.wrapped.image, options: [.new]) { [weak self] _, _ in + guard let self = self else { + return + } self.invalidateIntrinsicContentSize() } } @@ -81,7 +84,10 @@ public class AnimatedImageViewWrapper : PlatformView { public required init?(coder: NSCoder) { super.init(coder: coder) addSubview(wrapped) - observation = observe(\.wrapped.image, options: [.new]) { _, _ in + observation = observe(\.wrapped.image, options: [.new]) { [weak self] _, _ in + guard let self = self else { + return + } self.invalidateIntrinsicContentSize() } } From 09e93afb1c05032badfa8f3d6394f610f20c36bf Mon Sep 17 00:00:00 2001 From: Yikai Deng Date: Thu, 26 Jun 2025 19:13:29 +0800 Subject: [PATCH 89/90] Xcode 26 Compliant --- Package.resolved | 4 ++-- Package.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Package.resolved b/Package.resolved index 09a660d1..2c5b6821 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git", "state": { "branch": null, - "revision": "73b9397cfbd902f606572964055464903b1d84c6", - "version": "5.19.0" + "revision": "b62cb63bf4ed1f04c961a56c9c6c9d5ab8524ec6", + "version": "5.21.1" } } ] diff --git a/Package.swift b/Package.swift index e14a6aac..8c48349f 100644 --- a/Package.swift +++ b/Package.swift @@ -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.10.0") + .package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.21.1") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. From 595bf7eb74f7a84ce02a59bbf6878575d6f35be0 Mon Sep 17 00:00:00 2001 From: Yikai Deng Date: Fri, 27 Jun 2025 14:00:31 +0800 Subject: [PATCH 90/90] Modified the missing files. --- Cartfile | 2 +- SDWebImageSwiftUI.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cartfile b/Cartfile index dcfaf022..54618334 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "SDWebImage/SDWebImage" ~> 5.10 \ No newline at end of file +github "SDWebImage/SDWebImage" ~> 5.21.1 diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 2c26725e..67c3835f 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -37,6 +37,6 @@ It brings all your favorite features from SDWebImage, like async image loading, } s.weak_frameworks = 'SwiftUI', 'Combine' - s.dependency 'SDWebImage', '~> 5.10' + s.dependency 'SDWebImage', '~> 5.21.1' s.swift_version = '5.3' end