Skip to content

Commit

Permalink
Merge pull request onevcat#1604 from onevcat/fix/kfimage-state-manage…
Browse files Browse the repository at this point in the history
…ment

Use `@State` for KFImage state management
  • Loading branch information
onevcat authored Jan 31, 2021
2 parents 31afdb8 + 9fa6a83 commit 9dfc06d
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 79 deletions.
3 changes: 1 addition & 2 deletions Demo/Demo/Kingfisher-Demo/SwiftUIViews/SwiftUIList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,11 @@ struct SwiftUIList : View {
}
.foregroundColor(.gray)
}
.fade(duration: 1)
.cancelOnDisappear(true)
.aspectRatio(contentMode: .fit)
.cornerRadius(20)
.frame(width: 300, height: 300)
.opacity(done || alreadyCached ? 1.0 : 0.3)
.animation(.linear(duration: 0.4))

Spacer()
}.padding(.vertical, 12)
Expand Down
9 changes: 9 additions & 0 deletions Demo/Demo/Kingfisher-Demo/SwiftUIViews/SwiftUIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,17 @@ struct SwiftUIView : View {

@State private var index = 1

@State private var blackWhite = false

var url: URL {
URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher-TestImages/master/DemoAppImage/Loading/kingfisher-\(self.index).jpg")!
}

var body: some View {
VStack {
KFImage(url)
.cacheOriginalImage()
.setProcessor(blackWhite ? BlackWhiteProcessor() : DefaultImageProcessor())
.onSuccess { r in
print("suc: \(r)")
}
Expand All @@ -49,6 +53,8 @@ struct SwiftUIView : View {
Image(systemName: "arrow.2.circlepath.circle")
.font(.largeTitle)
}
.fade(duration: 1)
.forceTransition()
.resizable()
.frame(width: 300, height: 300)
.cornerRadius(20)
Expand All @@ -57,6 +63,9 @@ struct SwiftUIView : View {
Button(action: {
self.index = (self.index % 10) + 1
}) { Text("Next Image") }
Button(action: {
self.blackWhite.toggle()
}) { Text("Black & White") }

}.navigationBarTitle(Text("Basic Image"), displayMode: .inline)
}
Expand Down
5 changes: 4 additions & 1 deletion Sources/Extensions/ImageView+Kingfisher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,10 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView {
switch options.transition {
case .none:
return false
#if !os(macOS)
#if os(macOS)
case .fade: // Fade is only a placeholder for SwiftUI on macOS.
return false
#else
default:
if options.forceTransition { return true }
if cacheType == .none { return true }
Expand Down
26 changes: 26 additions & 0 deletions Sources/General/ImageSource/Source.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,32 @@ public enum Source {
}
}

extension Source: Hashable {
public static func == (lhs: Source, rhs: Source) -> Bool {
switch (lhs, rhs) {
case (.network(let r1), .network(let r2)):
return r1.cacheKey == r2.cacheKey && r1.downloadURL == r2.downloadURL
case (.provider(let p1), .provider(let p2)):
return p1.cacheKey == p2.cacheKey && p1.contentURL == p2.contentURL
case (.provider(_), .network(_)):
return false
case (.network(_), .provider(_)):
return false
}
}

public func hash(into hasher: inout Hasher) {
switch self {
case .network(let r):
hasher.combine(r.cacheKey)
hasher.combine(r.downloadURL)
case .provider(let p):
hasher.combine(p.cacheKey)
hasher.combine(p.contentURL)
}
}
}

extension Source {
var asResource: Resource? {
guard case .network(let resource) = self else {
Expand Down
8 changes: 0 additions & 8 deletions Sources/General/KF.swift
Original file line number Diff line number Diff line change
Expand Up @@ -333,14 +333,6 @@ extension KF.Builder {
}
#endif

/// Sets whether the image setting for an image view should happen with transition even when retrieved from cache.
/// - Parameter enabled: Enable the force transition or not.
/// - Returns: A `KF.Builder` with changes applied.
public func forceTransition(_ enabled: Bool = true) -> Self {
options.forceTransition = enabled
return self
}

/// Sets whether keeping the existing image of image view while setting another image to it.
/// - Parameter enabled: Whether the existing image should be kept.
/// - Returns: A `KF.Builder` with changes applied.
Expand Down
21 changes: 15 additions & 6 deletions Sources/General/KFOptionsSetter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ extension KF.Builder: KFOptionSetter {
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension KFImage: KFOptionSetter {
public var options: KingfisherParsedOptionsInfo {
get { binder.options }
nonmutating set { binder.options = newValue }
get { context.binder.options }
nonmutating set { context.binder.options = newValue }
}

public var onFailureDelegate: Delegate<KingfisherError, Void> { binder.onFailureDelegate }
public var onSuccessDelegate: Delegate<RetrieveImageResult, Void> { binder.onSuccessDelegate }
public var onProgressDelegate: Delegate<(Int64, Int64), Void> { binder.onProgressDelegate }
public var onFailureDelegate: Delegate<KingfisherError, Void> { context.binder.onFailureDelegate }
public var onSuccessDelegate: Delegate<RetrieveImageResult, Void> { context.binder.onSuccessDelegate }
public var onProgressDelegate: Delegate<(Int64, Int64), Void> { context.binder.onProgressDelegate }

public var delegateObserver: AnyObject { binder }
public var delegateObserver: AnyObject { context.binder }
}
#endif

Expand Down Expand Up @@ -339,6 +339,15 @@ extension KFOptionSetter {
options.lowDataModeSource = source
return self
}

/// Sets whether the image setting for an image view should happen with transition even when retrieved from cache.
/// - Parameter enabled: Enable the force transition or not.
/// - Returns: A `KF.Builder` with changes applied.
public func forceTransition(_ enabled: Bool = true) -> Self {
options.forceTransition = enabled
return self
}

}

// MARK: - Request Modifier
Expand Down
3 changes: 3 additions & 0 deletions Sources/Image/ImageTransition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import Foundation
#if os(iOS) || os(tvOS)
import UIKit

Expand Down Expand Up @@ -111,5 +112,7 @@ public enum ImageTransition {
// Just a placeholder for compiling on macOS.
public enum ImageTransition {
case none
/// This is a placeholder on macOS now. It is for SwiftUI (KFImage) to identify the fade option only.
case fade(TimeInterval)
}
#endif
30 changes: 23 additions & 7 deletions Sources/SwiftUI/ImageBinder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ extension KFImage {

/// Represents a binder for `KFImage`. It takes responsibility as an `ObjectBinding` and performs
/// image downloading and progress reporting based on `KingfisherManager`.
class ImageBinder: ObservableObject {
class ImageBinder {

let source: Source?
var options = KingfisherParsedOptionsInfo(KingfisherManager.shared.defaultOptions)
Expand All @@ -48,8 +48,6 @@ extension KFImage {

var isLoaded: Binding<Bool>

@Published var image: KFCrossPlatformImage?

@available(*, deprecated, message: "The `options` version is deprecated And will be removed soon.")
init(source: Source?, options: KingfisherOptionsInfo? = nil, isLoaded: Binding<Bool>) {
self.source = source
Expand All @@ -61,7 +59,6 @@ extension KFImage {
[.loadDiskFileSynchronously]
)
self.isLoaded = isLoaded
self.image = nil
}

init(source: Source?, isLoaded: Binding<Bool>) {
Expand All @@ -73,10 +70,9 @@ extension KFImage {
[.loadDiskFileSynchronously]
)
self.isLoaded = isLoaded
self.image = nil
}

func start() {
func start(_ done: @escaping (Result<RetrieveImageResult, KingfisherError>) -> Void) {

guard !loadingOrSucceeded else { return }

Expand All @@ -103,21 +99,29 @@ extension KFImage {
self.downloadTask = nil
switch result {
case .success(let value):

// The normalized version of image is used to solve #1395
// It should be not necessary if SwiftUI.Image can handle resizing correctly when created
// by `Image.init(uiImage:)`. (The orientation information should be already contained in
// a `UIImage`)
// https://github.com/onevcat/Kingfisher/issues/1395
let image = value.image.kf.normalized
let r = RetrieveImageResult(
image: image, cacheType: value.cacheType, source: value.source, originalSource: value.originalSource
)
CallbackQueue.mainCurrentOrAsync.execute {
self.image = image
done(.success(r))
}

CallbackQueue.mainAsync.execute {
self.isLoaded.wrappedValue = true
self.onSuccessDelegate.call(value)
}
case .failure(let error):
self.loadingOrSucceeded = false
CallbackQueue.mainCurrentOrAsync.execute {
done(.failure(error))
}
CallbackQueue.mainAsync.execute {
self.onFailureDelegate.call(error)
}
Expand All @@ -131,4 +135,16 @@ extension KFImage {
}
}
}

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension KFImage.ImageBinder: Hashable {
static func == (lhs: KFImage.ImageBinder, rhs: KFImage.ImageBinder) -> Bool {
return lhs === rhs
}

func hash(into hasher: inout Hasher) {
hasher.combine(source)
hasher.combine(options.processor.identifier)
}
}
#endif
Loading

0 comments on commit 9dfc06d

Please sign in to comment.