Sometimes when custom a view in SwiftUI, it require some parameters to change the appearance of it which is not possible to be achieve by ViewModifier. Like the demo shown below, I want to set text color and image color separately.
struct CustomView: View {
var imageColor: Color
var textColor: Color
init(imageColor: Color = .primary, textColor: Color = .primary) {
self.imageColor = imageColor
self.textColor = textColor
}
var body: some View {
VStack {
Image(systemName: "star")
.foregroundColor(imageColor)
Text("Hello World")
.foregroundColor(textColor)
}
}
}
Though providing parameters via init() works. But it's ugly when there're tens of parameters. It could be a nightmare to invoke it.
SwiftUI uses modifiers to solve this problem. It couldn't be better if I can custom my own.
Solution1 (but not working)
struct CustomView: View {
var imageColor: Color = .primary
var textColor: Color = .primary
var body: some View {
VStack {
Image(systemName: "star")
.foregroundColor(imageColor)
Text("Hello World")
.foregroundColor(textColor)
}
}
mutating func imageColor(_ imageColor: Color) -> CustomView {
self.imageColor = imageColor
return self
}
mutating func textColor(_ textColor: Color) -> CustomView {
self.textColor = textColor
return self
}
}
But there's an error when compiling Cannot use mutating member on immutable value: function call returns immutable value.
CustomView()
.imageColor(.red) // error
.textColor(.blue) // error
Then I changed the var to @State var in CustomView struct. But the value will never change when calling it. The reason I found is discussed here: https://forums.swift.org/t/im-so-confused-why-state-var-inside-a-view-do-not-change-when-you-call-a-method-on-this-view-from-some-outer-view/31944
Solution2 (but inefficient)
struct CustomView: View {
var imageColor: Color
var textColor: Color
init(imageColor: Color = .primary, textColor: Color = .primary) {
self.imageColor = imageColor
self.textColor = textColor
}
var body: some View {
VStack {
Image(systemName: "star")
.foregroundColor(imageColor)
Text("Hello World")
.foregroundColor(textColor)
}
}
func imageColor(_ imageColor: Color) -> CustomView {
return CustomView(imageColor: imageColor, textColor: self.textColor)
}
func textColor(_ textColor: Color) -> CustomView {
return CustomView(imageColor: self.imageColor, textColor: textColor)
}
}
Obviously, it takes a little more system resource as shown in below monitor of Xcode. So how can I add modifiers like these efficiently? How Apple SwifUI's built-in modifiers made this?

@Statework. The actual value will never change. The reason are described here: forums.swift.org/t/…EnvironmentKeyand extension it inEnvironmentValues, then set that environment value inside modifier-like functions likeenvironment(\.customEnvironment, value). But it's not ideal, since the environment value will affect the child views.