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:
commit
0dcefe507f
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue