From 26f75715c7492841e5373906b1baca1e32a98ff4 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 1 Jul 2024 16:41:02 +0800 Subject: [PATCH 1/2] 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 f42eccb..eb5ec27 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 7cc6fbe..3dab6a3 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 2/2] 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 6654372..4e5a7a7 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 e019881..ff42fdf 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() + } } }