Files
meterios/common/aux/AsyncImageWithPlaceholder.swift

64 lines
2.1 KiB
Swift

//
// AsyncImageWithPlaceholder.swift
// meterios
//
// (c) 2025 Martin "maride" Dessauer
//
import Foundation
import SwiftUI
import OSLog
/// **AsyncImageWithPlaceholder** wraps AsyncImage, showing a Spinner while loading and a question mark in case of errors.
struct AsyncImageWithPlaceholder : View {
private var url: URL?
private var width: CGFloat
private var height: CGFloat
// Access the current color scheme (that is, e.g.: dark mode, light mode) environment value
@Environment(\.colorScheme) var colorScheme
// baseShape and baseShapeView resemble the "background" (while loading, and in case of errors, and for the image itself)
private var baseShape : some Shape = RoundedRectangle(cornerRadius: 8.0)
var baseShapeView : some View {
baseShape
.foregroundStyle(colorScheme == .dark ? .black : .white)
.frame(width: width, height: height)
}
// Creates a new instance of AsyncImageWithPlaceholder, loading the image pointed to by url, with exact measures of width by height
init(url: URL? = nil, width: CGFloat, height: CGFloat) {
self.url = url
self.width = width
self.height = height
}
var body: some View {
AsyncImage(url: self.url) { phase in
if let image = phase.image {
// Image loaded
image
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: self.width, height: self.height)
.clipShape(baseShape)
} else if phase.error != nil {
// Error, log and display a dummy image (question mark)
let _ = Logger().error("Error loading image: \(phase.error)")
ZStack {
baseShapeView
Text("?")
.fontWeight(.bold)
.foregroundStyle(.gray)
}
} else {
// Loading Placeholder
ZStack {
baseShapeView
ProgressView()
}
}
}.fixedSize()
}
}