Fix the issue sometime the `WebImage` appear/disappear logic wrong. Using UIKit/AppKit to detect the visibility

This commit is contained in:
DreamPiggy 2021-02-23 13:24:49 +08:00
parent 5832951dd9
commit b512e31d28
3 changed files with 114 additions and 13 deletions

View File

@ -75,6 +75,14 @@
32C43E3322FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */; };
32C43E3422FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */; };
32C43E3522FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */; };
32CBA78025E4D7D800C6A8DC /* ImagePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */; };
32CBA78125E4D7D800C6A8DC /* ImagePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */; };
32CBA78225E4D7D800C6A8DC /* ImagePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */; };
32CBA78325E4D7D800C6A8DC /* ImagePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */; };
32CBA78425E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77F25E4D7D800C6A8DC /* SwiftUICompatibility.swift */; };
32CBA78525E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77F25E4D7D800C6A8DC /* SwiftUICompatibility.swift */; };
32CBA78625E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77F25E4D7D800C6A8DC /* SwiftUICompatibility.swift */; };
32CBA78725E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77F25E4D7D800C6A8DC /* SwiftUICompatibility.swift */; };
32D26A022446B546005905DA /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; };
32D26A032446B546005905DA /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; };
32D26A042446B546005905DA /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; };
@ -137,6 +145,8 @@
32C43E2922FD586200BE87F5 /* SDWebImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDWebImage.framework; path = Carthage/Build/tvOS/SDWebImage.framework; sourceTree = "<group>"; };
32C43E2D22FD586E00BE87F5 /* SDWebImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDWebImage.framework; path = Carthage/Build/watchOS/SDWebImage.framework; sourceTree = "<group>"; };
32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDWebImageSwiftUI.swift; sourceTree = "<group>"; };
32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImagePlayer.swift; sourceTree = "<group>"; };
32CBA77F25E4D7D800C6A8DC /* SwiftUICompatibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUICompatibility.swift; sourceTree = "<group>"; };
32D26A012446B546005905DA /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = "<group>"; };
32ED4825242A13030053338E /* ImageManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageManagerTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -279,6 +289,8 @@
32B933E323659A0700BB7CAD /* Transition */,
326099472362E09E006EBB22 /* Indicator */,
32C43DDC22FD54C600BE87F5 /* ImageManager.swift */,
32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */,
32CBA77F25E4D7D800C6A8DC /* SwiftUICompatibility.swift */,
32C43DDE22FD54C600BE87F5 /* WebImage.swift */,
32C43DDF22FD54C600BE87F5 /* AnimatedImage.swift */,
32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */,
@ -696,6 +708,8 @@
buildActionMask = 2147483647;
files = (
32B933E523659A1900BB7CAD /* Transition.swift in Sources */,
32CBA78025E4D7D800C6A8DC /* ImagePlayer.swift in Sources */,
32CBA78425E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */,
32C43E1722FD583700BE87F5 /* WebImage.swift in Sources */,
326B848C236335400011BDFB /* ProgressIndicator.swift in Sources */,
326B84822363350C0011BDFB /* Indicator.swift in Sources */,
@ -713,6 +727,8 @@
buildActionMask = 2147483647;
files = (
32B933E623659A1900BB7CAD /* Transition.swift in Sources */,
32CBA78125E4D7D800C6A8DC /* ImagePlayer.swift in Sources */,
32CBA78525E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */,
32C43E1A22FD583700BE87F5 /* WebImage.swift in Sources */,
326B848D236335400011BDFB /* ProgressIndicator.swift in Sources */,
326B84832363350C0011BDFB /* Indicator.swift in Sources */,
@ -730,6 +746,8 @@
buildActionMask = 2147483647;
files = (
32B933E723659A1900BB7CAD /* Transition.swift in Sources */,
32CBA78225E4D7D800C6A8DC /* ImagePlayer.swift in Sources */,
32CBA78625E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */,
32C43E1D22FD583800BE87F5 /* WebImage.swift in Sources */,
326B848E236335400011BDFB /* ProgressIndicator.swift in Sources */,
326B84842363350C0011BDFB /* Indicator.swift in Sources */,
@ -747,6 +765,8 @@
buildActionMask = 2147483647;
files = (
32B933E823659A1900BB7CAD /* Transition.swift in Sources */,
32CBA78325E4D7D800C6A8DC /* ImagePlayer.swift in Sources */,
32CBA78725E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */,
32C43E2022FD583800BE87F5 /* WebImage.swift in Sources */,
326B848F236335400011BDFB /* ProgressIndicator.swift in Sources */,
326B84852363350C0011BDFB /* Indicator.swift in Sources */,

View File

@ -0,0 +1,83 @@
/*
* This file is part of the SDWebImage package.
* (c) DreamPiggy <lizhuoli1126@126.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import Foundation
import SwiftUI
#if os(iOS) || os(tvOS) || os(macOS)
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
struct PlatformAppear: PlatformViewRepresentable {
let appearAction: () -> Void
let disappearAction: () -> Void
#if os(iOS) || os(tvOS)
func makeUIView(context: Context) -> some UIView {
let view = PlatformAppearView()
view.appearAction = appearAction
view.disappearAction = disappearAction
return view
}
func updateUIView(_ uiView: UIViewType, context: Context) {}
#endif
#if os(macOS)
func makeNSView(context: Context) -> some NSView {
let view = PlatformAppearView()
view.appearAction = appearAction
view.disappearAction = disappearAction
return view
}
func updateNSView(_ nsView: NSViewType, context: Context) {}
#endif
}
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
class PlatformAppearView: PlatformView {
var appearAction: () -> Void = {}
var disappearAction: () -> Void = {}
#if os(iOS) || os(tvOS)
override func willMove(toWindow newWindow: UIWindow?) {
if newWindow != nil {
appearAction()
} else {
disappearAction()
}
}
#endif
#if os(macOS)
override func viewWillMove(toWindow newWindow: NSWindow?) {
if newWindow != nil {
appearAction()
} else {
disappearAction()
}
}
#endif
}
#endif
extension View {
/// Used UIKit/AppKit behavior to detect the SwiftUI view's visibility.
/// This hack is because of SwiftUI 1.0/2.0 buggy behavior. The built-in `onAppear` and `onDisappear` is so massive on some cases. Where UIKit/AppKit is solid.
/// - Parameters:
/// - appear: The action when view appears
/// - disappear: The action when view disappears
/// - Returns: Some view
func onPlatformAppear(appear: @escaping () -> Void = {}, disappear: @escaping () -> Void = {}) -> some View {
#if os(iOS) || os(tvOS) || os(macOS)
return self.background(PlatformAppear(appearAction: appear, disappearAction: disappear))
#else
return self.onAppear(perform: appear).onDisappear(perform: disappear)
#endif
}
}

View File

@ -65,23 +65,22 @@ public struct WebImage : View {
if isAnimating && !imageManager.isIncremental {
if imagePlayer.currentFrame != nil {
configure(image: imagePlayer.currentFrame!)
.onAppear {
imagePlayer.startPlaying()
}
.onDisappear {
.onPlatformAppear(appear: {
self.imagePlayer.startPlaying()
}, disappear: {
if self.pausable {
imagePlayer.pausePlaying()
self.imagePlayer.pausePlaying()
} else {
imagePlayer.stopPlaying()
self.imagePlayer.stopPlaying()
}
if self.purgeable {
imagePlayer.clearFrameBuffer()
self.imagePlayer.clearFrameBuffer()
}
}
})
} else {
configure(image: imageManager.image!)
.onReceive(imageManager.$image) { image in
imagePlayer.setupPlayer(image: image)
self.imagePlayer.setupPlayer(image: image)
}
}
} else {
@ -94,7 +93,7 @@ public struct WebImage : View {
} else {
setupPlaceholder()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.onAppear {
.onPlatformAppear(appear: {
// Load remote image when first appear
if self.imageManager.isFirstLoad {
self.imageManager.load()
@ -105,14 +104,13 @@ public struct WebImage : View {
if self.imageManager.image == nil && !self.imageManager.isIncremental {
self.imageManager.load()
}
}
.onDisappear {
}, disappear: {
guard self.cancelOnDisappear else { return }
// When using prorgessive loading, the previous partial image will cause onDisappear. Filter this case
if self.imageManager.image == nil && !self.imageManager.isIncremental {
self.imageManager.cancel()
}
}
})
}
}
}