I would like to create a view modifier that invokes Image.renderingMode(.template)
if some conditions (based on the environment) are met. Unfortunately, .renderingMode
is not defined on View
, but only on Image
, and therefore I cannot invoke it on Content
since Content is an opaque type that only conforms to View and therefore only supports functions declared on View
.
extension SemanticColor {
func realizedColor(itemStyle: ItemStyle) -> Color? {
switch itemStyle {
case .highlight:
return .white
case .warning:
return .black
case .default:
return nil
}
}
}
struct SemanticColorViewModifier: ViewModifier {
@Environment(\.itemStyle)
private var itemStyle: ItemStyle
var semanticColor: SemanticColor
func body(content: Content) -> some View {
let color: self.semanticColor.realizedColor(itemStyle: self.itemStyle)
// TODO: apply Image.renderingMode(.template) if color != nil
return content.foregroundColor(color)
}
}
public extension View {
func semanticForegroundColor(_ color: SemanticColor) -> some View {
return self.modifier(SemanticColorViewModifier(semanticColor: color))
}
}
I tried to create a custom ImageModifier inspired by this answer from jboi to SwiftUI: ViewModifier where content is an Image, but that did fall short because body is not re-evaluated once the state changes.
My current workaround is to declare a .semanticColor
function on View
that uses the above ViewModifier sans .renderingMode
, and use a nested View that duplicates the logic for Images.
struct SematicallyColoredImage: View {
init(image: Image, semanticColor: SemanticColor) {
self.image = image
self.semanticColor = semanticColor
}
@Environment(\.itemStyle)
private var itemStyle: ItemStyle
private let image: Image
private let semanticColor: SemanticColor
var body: some View {
if let color = self.semanticColor.realizedColor(itemStyle: self.itemStyle) {
return AnyView(self.image.renderingMode(.template).foregroundColor(color))
} else {
return AnyView(self.image)
}
}
}
public extension Image {
func semanticForegroundColor(_ color: SemanticColor) -> some View {
return SematicallyColoredImage(image: self, semanticColor: color)
}
}
This code works, but it feels redundant and problematic: If I invoke .semanticForegroundColor
on an Image after another modifier returns some View
, then the result does not take modify the rendering mode, whereas it does if I invoke it if the type is still Image
.