From 66ca5fbb83b8df5c09c382ab3f5e5f4f7ee57b27 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 22 Feb 2021 17:05:56 +0800 Subject: [PATCH] Update the logic to be compatible with LazyVStack's resetting status behavior --- SDWebImageSwiftUI/Classes/AnimatedImage.swift | 67 ++++++++++++------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index ff17f01..f442f6e 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -44,7 +44,7 @@ final class AnimatedLoadingModel : ObservableObject, IndicatorReportable { @Published var isLoading: Bool = false // whether network is loading or cache is querying, should only be used for indicator binding @Published var progress: Double = 0 // network progress, should only be used for indicator binding - /// Used for loading status recording to avoid recursive `updateView`. There are 3 types of loading (Name/Data/URL) + /// Used for loading status recording to avoid recursive `updateView`. There are 3 types of loading (Name/Data/URL) @Published var imageName: String? @Published var imageData: Data? @Published var imageURL: URL? @@ -206,12 +206,27 @@ public struct AnimatedImage : PlatformViewRepresentable { } #endif - func loadImage(_ view: AnimatedImageViewWrapper, context: Context) { - let operationKey = NSStringFromClass(type(of: view.wrapped)) - let currentOperation = view.wrapped.sd_imageLoadOperation(forKey: operationKey) - if currentOperation != nil { - return + func setupIndicator(_ view: AnimatedImageViewWrapper, context: Context) { + view.wrapped.sd_imageIndicator = imageConfiguration.indicator + view.wrapped.sd_imageTransition = imageConfiguration.transition + if let placeholderView = imageConfiguration.placeholderView { + placeholderView.removeFromSuperview() + placeholderView.isHidden = true + // Placeholder View should below the Indicator View + if let indicatorView = imageConfiguration.indicator?.indicatorView { + #if os(macOS) + view.wrapped.addSubview(placeholderView, positioned: .below, relativeTo: indicatorView) + #else + view.wrapped.insertSubview(placeholderView, belowSubview: indicatorView) + #endif + } else { + view.wrapped.addSubview(placeholderView) + } + placeholderView.bindFrameToSuperviewBounds() } + } + + func loadImage(_ view: AnimatedImageViewWrapper, context: Context) { self.imageLoading.isLoading = true let options = imageModel.webOptions if options.contains(.delayPlaceholder) { @@ -234,9 +249,9 @@ 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 + // 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 Xcode 11.3 bug, the @Published does not trigger another `updateUIView` call + // 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 = AnimatedImage.findHostingView(from: view) { if let _ = hostingView.window { @@ -284,26 +299,28 @@ public struct AnimatedImage : PlatformViewRepresentable { let image = SDAnimatedImage(data: data, scale: imageModel.scale) imageLoading.imageData = data view.wrapped.image = image - } else if let url = imageModel.url, url != imageLoading.imageURL { - imageLoading.imageURL = url - view.wrapped.sd_imageIndicator = imageConfiguration.indicator - view.wrapped.sd_imageTransition = imageConfiguration.transition - if let placeholderView = imageConfiguration.placeholderView { - placeholderView.removeFromSuperview() - placeholderView.isHidden = true - // Placeholder View should below the Indicator View - if let indicatorView = imageConfiguration.indicator?.indicatorView { - #if os(macOS) - view.wrapped.addSubview(placeholderView, positioned: .below, relativeTo: indicatorView) - #else - view.wrapped.insertSubview(placeholderView, belowSubview: indicatorView) - #endif + } else if let url = imageModel.url { + // Determine if image already been loaded and URL is match + var shouldLoad: Bool + if url != imageLoading.imageURL { + // Change the URL, need new loading + shouldLoad = true + imageLoading.imageURL = url + } else { + // Same URL, check if already loaded + if imageLoading.isLoading { + shouldLoad = false + } else if let image = imageLoading.image { + shouldLoad = false + view.wrapped.image = image } else { - view.wrapped.addSubview(placeholderView) + shouldLoad = true } - placeholderView.bindFrameToSuperviewBounds() } - loadImage(view, context: context) + if shouldLoad { + setupIndicator(view, context: context) + loadImage(view, context: context) + } } #if os(macOS)