swiftuiswiftui-stateviewmodifier

SwiftUI ViewModifier for custom View


Is there a way to create a modifier to update a @State private var in the view being modified?

I have a custom view that returns either a Text with a "dynamic" background color OR a Circle with a "dynamic" foreground color.

struct ChildView: View {
    var theText = ""
    
    @State private var color = Color(.purple)
    
    var body: some View {
        HStack {
            if theText.isEmpty {          // If there's no theText, a Circle is created
                Circle()
                    .foregroundColor(color)
                    .frame(width: 100, height: 100)
            } else {                      // If theText is provided, a Text is created
                Text(theText)
                    .padding()
                    .background(RoundedRectangle(cornerRadius: 25.0)
                                    .foregroundColor(color))
                    .foregroundColor(.white)
            }
        }
    }
}

I re-use this view in different parts around my app. As you can see, the only parameter I need to specify is theText. So, the possible ways to create this ChildView are as follows:

struct SomeParentView: View {
    var body: some View {
        VStack(spacing: 20) {
            ChildView()   // <- Will create a circle

            ChildView(theText: "Hello world!")   // <- Will create a text with background
        }
    }
}

enter image description here

Nothing fancy so far. Now, what I need is to create (maybe) a modifier or the like so that in the parent views I can change the value of that @State private var color from .red to other color if I need more customization on that ChildView. Example of what I'm trying to achieve:

struct SomeOtherParentView: View {
    var body: some View {
        HStack(spacing: 20) {
            ChildView()

            ChildView(theText: "Hello world!")
                .someModifierOrTheLike(color: Color.green)   // <- what I think I need
        }
    }
}

I know I could just remove the private keyword from that var and pass the color as parameter in the constructor (ex: ChildView(theText: "Hello World", color: .green)), but I don't think that's the way to solve this problem, because if I need more customization on the child view I'd end up with a very large constructor.

So, Any ideas on how to achieve what I'm looking for? Hope I explained myself :) Thanks!!!


Solution

  • It is your view and modifiers are just functions that generate another, modified, view, so... here is some possible simple way to achieve what you want.

    Tested with Xcode 12 / iOS 14

    demo

    struct ChildView: View {
        var theText = ""
        
        @State private var color = Color(.purple)
        
        var body: some View {
            HStack {
                if theText.isEmpty {          // If there's no theText, a Circle is created
                    Circle()
                        .foregroundColor(color)
                        .frame(width: 100, height: 100)
                } else {                      // If theText is provided, a Text is created
                    Text(theText)
                        .padding()
                        .background(RoundedRectangle(cornerRadius: 25.0)
                                        .foregroundColor(color))
                        .foregroundColor(.white)
                }
            }
        }
        
        // simply modify self, as self is just a value
        public func someModifierOrTheLike(color: Color) -> some View {
            var view = self
            view._color = State(initialValue: color)
            return view.id(UUID())
        }
    }