Caps-iOS/Caps/Views/CachedCapImage.swift
2022-06-21 19:38:51 +02:00

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)
}
}
}