Files
Cable/Cable/Loads/LoadIconView.swift
Stefan Lange-Hegermann 6258a6a66f more consitancy
2025-10-22 22:43:03 +02:00

78 lines
2.3 KiB
Swift

import Foundation
import SwiftUI
import UIKit
struct LoadIconView: View {
let remoteIconURLString: String?
let fallbackSystemName: String
let fallbackColor: Color
let size: CGFloat
private var cornerRadius: CGFloat {
max(6, size / 4)
}
@State private var cachedImage: Image?
@State private var isLoading = false
@State private var hasAttemptedLoad = false
var body: some View {
Group {
if let cachedImage {
cachedImage
.resizable()
.scaledToFit()
.frame(width: size, height: size)
.clipShape(RoundedRectangle(cornerRadius: cornerRadius))
} else if let url = remoteURL, !hasAttemptedLoad {
ProgressView()
.frame(width: size, height: size)
.task(id: url) {
await loadImage(from: url)
}
} else {
fallbackView
}
}
.frame(width: size, height: size)
.onChange(of: remoteIconURLString) { oldValue, newValue in
guard oldValue != newValue else { return }
cachedImage = nil
hasAttemptedLoad = false
}
}
private var fallbackView: some View {
ZStack {
RoundedRectangle(cornerRadius: cornerRadius)
.fill(fallbackColor)
Image(systemName: fallbackSystemName.isEmpty ? "lightbulb" : fallbackSystemName)
.font(.system(size: size * 0.5))
.foregroundColor(.white)
}
}
private var remoteURL: URL? {
guard let remoteIconURLString, let url = URL(string: remoteIconURLString) else { return nil }
return url
}
private func loadImage(from url: URL) async {
guard !isLoading else { return }
isLoading = true
defer { isLoading = false }
if let uiImage = try? await IconCache.shared.image(for: url) {
await MainActor.run {
cachedImage = Image(uiImage: uiImage)
hasAttemptedLoad = true
}
} else {
await MainActor.run {
cachedImage = nil
hasAttemptedLoad = true
}
}
}
}