80 lines
2.6 KiB
Swift
80 lines
2.6 KiB
Swift
|
import SwiftUI
|
||
|
|
||
|
struct CachedCapImage<Content, T>: View where Content: View, T: Equatable {
|
||
|
|
||
|
@State private var phase: AsyncImagePhase
|
||
|
|
||
|
let id: T
|
||
|
|
||
|
let check: () -> UIImage?
|
||
|
|
||
|
let fetch: () async -> UIImage?
|
||
|
|
||
|
private let transaction: Transaction
|
||
|
|
||
|
private let content: (AsyncImagePhase) -> Content
|
||
|
|
||
|
var body: some View {
|
||
|
content(phase)
|
||
|
.task(id: id, load)
|
||
|
}
|
||
|
|
||
|
init<I, P>(_ id: T, _ image: CapImage, cache: ImageCache, @ViewBuilder content: @escaping (Image) -> I, @ViewBuilder placeholder: @escaping () -> P) where Content == _ConditionalContent<I, P>, I : View, P : View {
|
||
|
self.init(id, image: image, cache: cache) { phase in
|
||
|
if let image = phase.image {
|
||
|
content(image)
|
||
|
} else {
|
||
|
placeholder()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
init(_ id: T, image: CapImage, cache: ImageCache, transaction: Transaction = Transaction(), @ViewBuilder content: @escaping (AsyncImagePhase) -> Content) {
|
||
|
self.init(id,
|
||
|
check: { cache.cachedImage(image) },
|
||
|
fetch: { await cache.image(image) },
|
||
|
transaction: transaction,
|
||
|
content: content)
|
||
|
}
|
||
|
|
||
|
init<I, P>(_ id: T, check: @escaping () -> UIImage?, fetch: @escaping () async -> UIImage?, @ViewBuilder content: @escaping (Image) -> I, @ViewBuilder placeholder: @escaping () -> P) where Content == _ConditionalContent<I, P>, I : View, P : View {
|
||
|
self.init(id, check: check, fetch: fetch) { phase in
|
||
|
if let image = phase.image {
|
||
|
content(image)
|
||
|
} else {
|
||
|
placeholder()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
init(_ id: T, check: @escaping () -> UIImage?, fetch: @escaping () async -> UIImage?, transaction: Transaction = Transaction(), @ViewBuilder content: @escaping (AsyncImagePhase) -> Content) {
|
||
|
self.id = id
|
||
|
self.check = check
|
||
|
self.fetch = fetch
|
||
|
self.transaction = transaction
|
||
|
self.content = content
|
||
|
|
||
|
self._phase = State(wrappedValue: .empty)
|
||
|
|
||
|
guard let image = check() else {
|
||
|
return
|
||
|
}
|
||
|
let wrapped = Image(uiImage: image)
|
||
|
self._phase = State(wrappedValue: .success(wrapped))
|
||
|
}
|
||
|
|
||
|
@Sendable
|
||
|
private func load() async {
|
||
|
guard let image = await fetch() else {
|
||
|
withAnimation(transaction.animation) {
|
||
|
phase = .empty
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
let wrapped = Image(uiImage: image)
|
||
|
withAnimation(transaction.animation) {
|
||
|
phase = .success(wrapped)
|
||
|
}
|
||
|
}
|
||
|
}
|