It took me way too long to figure out how to achieve following:
I wanted API to look something like
DocumentIcon(icon: .systemImage("creditcard"))
.font(.largeTitle) // optional font
I want to make sure that created system image respects externally passed font or defaults to .body
Additionally it maintains 5pt padding
It took multiple attempts of .aspectRatio(1, contentMode: …)
and GeometryReader { geo in … }
to finally arrive at this solution
struct SquareContentView<Content: View>: View {
let content: Content
@State private var sideLength: CGFloat? = nil
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
VStack {
content
}
.background(
GeometryReader { geometry in
Color.clear.onAppear {
let dimension = max(geometry.size.width, geometry.size.height)
sideLength = dimension
}
}
)
.frame(width: sideLength, height: sideLength)
}
}
The callsite component looks like so:
struct DocumentIcon: View {
var title: String
@State var icon: Document.Icon
@Environment(\.font) private var maybeFont
private var font: Font {
maybeFont ?? .body
}
var body: some View {
SquareContentView {
Text("X") // we use X to make sure icons are the same size regardless of icon dimension
.disabled(true)
.foregroundColor(.clear)
.overlay {
Group {
switch icon {
case .letter:
Text(String(title.first ?? "?"))
case .systemImage(let systemName):
Image(systemName: systemName)
}
}
.font(font)
.foregroundColor(.white)
}
.padding(font.scaledValue(10))
}
.background(.orange)
.cornerRadius(font.scaledValue(5))
}
}
Hope it'll save someone time ✌️
In the future I'd love to make following components dependant on the font set externally
- padding between image and the bounds of the component
- corner radius of the component