Merge pull request #92 from SDWebImage/fix_query_image_extra_times

Fix the issue when using WebImage with some transition like scaleEffect, each time the new state update will cause unused image fetching
This commit is contained in:
DreamPiggy 2020-04-01 15:39:33 +08:00 committed by GitHub
commit 0dcefe507f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 9 deletions

View File

@ -38,7 +38,7 @@ struct DetailView: View {
let url: String let url: String
let animated: Bool let animated: Bool
@State var isAnimating: Bool = true @State var isAnimating: Bool = true
@State var lastScaleValue: CGFloat = 1.0 @State var lastScale: CGFloat = 1.0
@State var scale: CGFloat = 1.0 @State var scale: CGFloat = 1.0
@Environment(\.presentationMode) var presentationMode @Environment(\.presentationMode) var presentationMode
@EnvironmentObject var settings: UserSettings @EnvironmentObject var settings: UserSettings
@ -75,12 +75,12 @@ struct DetailView: View {
return contentView() return contentView()
.scaleEffect(self.scale) .scaleEffect(self.scale)
.gesture(MagnificationGesture(minimumScaleDelta: 0.1).onChanged { value in .gesture(MagnificationGesture(minimumScaleDelta: 0.1).onChanged { value in
let delta = value / self.lastScaleValue let delta = value / self.lastScale
self.lastScaleValue = value self.lastScale = value
let newScale = self.scale * delta let newScale = self.scale * delta
self.scale = min(max(newScale, 0.5), 2) self.scale = min(max(newScale, 0.5), 2)
}.onEnded { value in }.onEnded { value in
self.lastScaleValue = 1.0 self.lastScale = 1.0
}) })
#endif #endif
#if os(tvOS) #if os(tvOS)

View File

@ -27,6 +27,7 @@ public final class ImageManager : ObservableObject {
var manager: SDWebImageManager var manager: SDWebImageManager
weak var currentOperation: SDWebImageOperation? = nil weak var currentOperation: SDWebImageOperation? = nil
var isFirstLoad: Bool = true // false after first call `load()` var isFirstLoad: Bool = true // false after first call `load()`
var isFirstPrefetch: Bool = true // false after first call `prefetch()`
var url: URL? var url: URL?
var options: SDWebImageOptions 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 // Completion Handler

View File

@ -60,11 +60,10 @@ public struct WebImage : View {
} }
public var body: some 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 prefetch the memory cache of image, to immediately render it on screen
// this can ensure we load the image, and display image synchronously when memory cache hit to avoid flashing // this solve the case when `onAppear` not been called, for example, some transaction indeterminate state, SwiftUI :)
// called once per struct, SDWebImage take care of the duplicated query if imageManager.isFirstPrefetch {
if imageManager.isFirstLoad { self.imageManager.prefetch()
imageManager.load()
} }
return Group { return Group {
if imageManager.image != nil { if imageManager.image != nil {
@ -109,6 +108,11 @@ public struct WebImage : View {
setupPlaceholder() setupPlaceholder()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.onAppear { .onAppear {
// load remote image when first appear
if self.imageManager.isFirstLoad {
self.imageManager.load()
return
}
guard self.retryOnAppear else { return } guard self.retryOnAppear else { return }
// When using prorgessive loading, the new partial image will cause onAppear. Filter this case // When using prorgessive loading, the new partial image will cause onAppear. Filter this case
if self.imageManager.image == nil && !self.imageManager.isIncremental { if self.imageManager.image == nil && !self.imageManager.isIncremental {