From d7c76397e91fb16cdfbf8c27ed40551f9baad6e5 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 1 Jul 2024 17:59:27 +0800 Subject: [PATCH] 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 66543720..4e5a7a75 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 e019881f..b5839536 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]) { observe, change in + self.invalidateIntrinsicContentSize() + } } public required init?(coder: NSCoder) { super.init(coder: coder) addSubview(wrapped) + observation = observe(\.wrapped.image, options: [.new]) { observe, change in + self.invalidateIntrinsicContentSize() + } } }