diff --git a/Example/SDWebImageSwiftUIDemo/DetailView.swift b/Example/SDWebImageSwiftUIDemo/DetailView.swift index eacbbd29..f3cf15ce 100644 --- a/Example/SDWebImageSwiftUIDemo/DetailView.swift +++ b/Example/SDWebImageSwiftUIDemo/DetailView.swift @@ -38,7 +38,7 @@ struct DetailView: View { let url: String let animated: Bool @State var isAnimating: Bool = true - @State var lastScaleValue: CGFloat = 1.0 + @State var lastScale: CGFloat = 1.0 @State var scale: CGFloat = 1.0 @Environment(\.presentationMode) var presentationMode @EnvironmentObject var settings: UserSettings @@ -75,12 +75,12 @@ struct DetailView: View { return contentView() .scaleEffect(self.scale) .gesture(MagnificationGesture(minimumScaleDelta: 0.1).onChanged { value in - let delta = value / self.lastScaleValue - self.lastScaleValue = value + let delta = value / self.lastScale + self.lastScale = value let newScale = self.scale * delta self.scale = min(max(newScale, 0.5), 2) }.onEnded { value in - self.lastScaleValue = 1.0 + self.lastScale = 1.0 }) #endif #if os(tvOS) diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index 77267698..5c913648 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -27,6 +27,7 @@ public final class ImageManager : ObservableObject { var manager: SDWebImageManager weak var currentOperation: SDWebImageOperation? = nil var isFirstLoad: Bool = true // false after first call `load()` + var isFirstPrefetch: Bool = true // false after first call `prefetch()` var url: URL? var options: SDWebImageOptions @@ -106,6 +107,45 @@ public final class ImageManager : ObservableObject { } } + /// Prefetch the initial state of image, currently query the memory cache only + func prefetch() { + isFirstPrefetch = false + // Use the options processor if provided + let options = self.options + var context = self.context + if let result = manager.optionsProcessor?.processedResult(for: url, options: options, context: context) { + context = result.context + } + // TODO: Remove transformer for cache calculation before SDWebImage 5.7.0, this is bug. Remove later + let transformer = (context?[.imageTransformer] as? SDImageTransformer) ?? manager.transformer + context?[.imageTransformer] = nil + // TODO: before SDWebImage 5.7.0, this is the SPI. Remove later + var key = manager.perform(Selector(("cacheKeyForURL:context:")), with: url, with: context)?.takeUnretainedValue() as? String + if let transformer = transformer { + key = SDTransformedKeyForKey(key, transformer.transformerKey) + } + // Shortcut for built-in cache + if let imageCache = manager.imageCache as? SDImageCache { + let image = imageCache.imageFromMemoryCache(forKey: key) + self.image = image + if let image = image { + self.successBlock?(image, .memory) + } + } else { + // This callback is synchronzied + manager.imageCache.containsImage(forKey: key, cacheType: .memory) { [unowned self] (cacheType) in + if cacheType == .memory { + self.manager.imageCache.queryImage(forKey: key, options: options, context: context) { [unowned self] (image, data, cacheType) in + self.image = image + if let image = image { + self.successBlock?(image, cacheType) + } + } + } + } + } + } + } // Completion Handler diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 424552de..ff10e021 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -60,11 +60,10 @@ public struct WebImage : View { } public var body: some View { - // load remote image when first called `body`, SwiftUI sometimes will create a new View struct without calling `onAppear` (like enter EditMode) :) - // this can ensure we load the image, and display image synchronously when memory cache hit to avoid flashing - // called once per struct, SDWebImage take care of the duplicated query - if imageManager.isFirstLoad { - imageManager.load() + // this prefetch the memory cache of image, to immediately render it on screen + // this solve the case when `onAppear` not been called, for example, some transaction indeterminate state, SwiftUI :) + if imageManager.isFirstPrefetch { + self.imageManager.prefetch() } return Group { if imageManager.image != nil { @@ -109,6 +108,11 @@ public struct WebImage : View { setupPlaceholder() .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) .onAppear { + // load remote image when first appear + if self.imageManager.isFirstLoad { + self.imageManager.load() + return + } guard self.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 {