Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

187
Views
How to assign default modifiers to a SwiftUI view?

I'm trying to make reusable custom views that themselves can be customized later. Ideally I'd be able to define default modifiers like Color, Font, etc that the Views would have without any customization, but allow these to be easily overwritten by additional modifiers used later on.

For example if I had the custom view:

struct MyCustomTextField: View {

  @Binding var text: String

  var body: some View {
    HStack {
      Image(systemName: "envelope").foregroundColor(.gray)
      TextField("", text: $text)
        .foregroundColor(.gray)
        .font(.system(.title))
    }
  }
}

The view would have default gray foreground color and the TextField would have title font. But now if I wanted to reuse this view and customize it for my specific use-case, I might want to override these modifiers like so:

struct ContentView: View {
  @State var text = "hello"

  var body: some View {
    MyCustomTextField(text: $text)
      .foregroundColor(.blue)
      .font(.system(.body))
  }
}

But these outside modifiers are not effective as the inner modifiers take precedent.

What's the best way to make custom views so that I can define default modifiers for their contents but still be able to override those defaults later?

Thanks

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

We should always remember that SwiftUI Views are value types, therefore you cannot change them after the initialization like you would do on UIKit. One possible way would be to inject it on the initialization. But if you wanna make it adjustable after the initialization (e.g to change the color upon pressing some other button), use the binding mechanism:

Add a @Binding to your MyCustomTextField, and use its value at the foregroundColor modifier.

struct MyCustomTextField: View {

    @Binding var text: String
    @Binding var foregroundColor: Color

    var body: some View {
      HStack {
        Image(systemName: "envelope").foregroundColor(.gray)
        TextField("", text: $text)
          .foregroundColor(foregroundColor)
          .font(.system(.title))
      }
    }
}

and then in the client view you can do as following:

struct MyView: View {
    @State var myText: String = ""

    @State var hasSpecialColor: Bool = false
    
    var foregroundColor: Binding<Color> {
        return Binding<Color>(get: {
            return hasSpecialColor ? Color.red : Color.black
        }, set: { newValue in
            hasSpecialColor = newValue == Color.red
        })
    }
    
    var body: some View {
        VStack {
            Text("Some Header Text")
            
            MyCustomTextField(text: $myText, foregroundColor: foregroundColor)
            
            Button("click to toggle color") {
                hasSpecialColor.toggle()
            }
        }
    }
}

now, everytime you will click the button, it will toggle hasSpecialColor (which is @State), and therefore the view will render and the value for the @Binding foregroundColor will change accordingly.

over 4 years ago · Santiago Trujillo Report

0

You can find the Font set by the nearest font modifier in the environment, so you can read it and provide a default like this:

struct MyCustomTextField: View {
  @Binding var text: String

  @Environment(\.font)
  var envFont: Font?

  var body: some View {
    HStack {
      Image(systemName: "envelope").foregroundColor(.gray)
      TextField("", text: $text)
        .foregroundColor(.gray)
        .font(envFont ?? .system(.title))
    }
  }
}

Unfortunately, the foregroundColor modifier doesn't store its setting in a (public) environment property, so you have to go a different route. One way is to provide a modifier directly on your custom view, like this:

struct MyCustomTextField: View {
    @Binding var text: String

    var _myColor: Color = .gray

    @Environment(\.font)
    var envFont: Font?

    func myColor(_ color: Color) -> Self {
        var copy = self
        copy._myColor = color
        return copy
    }

    var body: some View {
        HStack {
            Image(systemName: "envelope").foregroundColor(.gray)
            TextField("", text: $text)
                .foregroundColor(_myColor)
                .font(envFont ?? .system(.title))
        }
    }
}

You can then use the myColor modifier directly on a MyCustomTextField like this:

PlaygroundPage.current.setLiveView(
    MyCustomTextField(text: .constant("hello"))
        .myColor(.red)
        .padding()
        .font(.body)
)

But you cannot use it on any enclosing view or after any non-MyCustomTextField-specific modifier. For example, this will not work:

PlaygroundPage.current.setLiveView(
    MyCustomTextField(text: .constant("hello"))
        .padding()
        .myColor(.red) // Error: Value of type 'some View' has no member 'myColor'
        .font(.body)
)

If you want that to work, then you need to store the custom color in the environment, like this:

struct MyColor: EnvironmentKey {
    static var defaultValue: Color? { nil }
}

extension EnvironmentValues {
    var myColor: Color? {
        get { self[MyColor.self] }
        set { self[MyColor.self] = newValue }
    }
}

extension View {
    func myColor(_ color: Color?) -> some View {
        return self.environment(\.myColor, color)
    }
}

struct MyCustomTextField: View {
    @Binding var text: String

    @Environment(\.myColor)
    var envColor: Color?

    @Environment(\.font)
    var envFont: Font?

    var body: some View {
        HStack {
            Image(systemName: "envelope").foregroundColor(.gray)
            TextField("", text: $text)
                .foregroundColor(envColor ?? .gray)
                .font(envFont ?? .system(.title))
        }
    }
}

And then you can use the myColor modifier on any view, and it will apply to all enclosed subviews:

PlaygroundPage.current.setLiveView(
    MyCustomTextField(text: .constant("hello"))
        .padding()
        .myColor(.red)
        .font(.body)
)
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!