85

I want to change the placeholder color of the TextField, but I can't find a method for it.

I tried to set foregroundColor and accentColor, but it doesn't change the placeholder color.

Here is the code:

TextField("Placeholder", $text)
    .foregroundColor(Color.red)
    .accentColor(Color.green)

Maybe there is no API for this yet?

Ilya Kharabet
  • 3,558
  • 2
  • 14
  • 26

5 Answers5

141

There is no api for it (yet). BUT YOU CAN:

Use a custom placeholder modifier to show any view as the holder of any other view! e.g:

TextField("", text: $text)
    .placeholder(when: text.isEmpty) {
        Text("Placeholder recreated").foregroundColor(.gray)
}

Demo1

It's a simple ZStack that you can in a View extension like:

extension View {
    func placeholder<Content: View>(
        when shouldShow: Bool,
        alignment: Alignment = .leading,
        @ViewBuilder placeholder: () -> Content) -> some View {

        ZStack(alignment: alignment) {
            placeholder().opacity(shouldShow ? 1 : 0)
            self
        }
    }
}

Now you can apply any kind of style to the placeholder like this gradient placeholder with image:

Demo2

✅ If you are interested, Here is how to apply resizable gradient on any view


The Art of the simplicity

Most of the time you need to pass just a string and a gray placeholder like:

TextField("", text: $text)
    .placeholder("Placeholder", when: text.isEmpty)

you can write a simple wrapper around the above extension for it:

extension View {
    func placeholder(
        _ text: String,
        when shouldShow: Bool,
        alignment: Alignment = .leading) -> some View {
            
        placeholder(when: shouldShow, alignment: alignment) { Text(text).foregroundColor(.gray) }
    }
}

Just like that

Mojtaba Hosseini
  • 71,072
  • 19
  • 226
  • 225
  • 5
    This doesnt seem to work on iOS14. TextField is blank, i,e, no placeholder – daihovey Oct 06 '20 at 06:14
  • 2
    Thank you, you saved my day! the default color was unreadable for me without you trick – Slamit Feb 15 '21 at 22:17
  • Cool idea, I simplified it to this for myself so it wasn't bloated: `func placeholder(when shouldShow: Bool, placeholder: String)` – Big_Chair Nov 12 '21 at 12:34
  • 1
    @Big_Chair I have added another extension for that, a simple helper that takes a String and sets the Text inside. – Mojtaba Hosseini Nov 12 '21 at 12:48
  • @daihovey that's seems because of ZStack leading alignment issue on iOS 14 Did you find the solution? – Evgeny Karkan May 13 '22 at 17:01
  • @MojtabaHosseini hi, out of interest - maybe you know how to workaround iOS 14 issue when placeholder is not visible? Seems that's related to ZStack leading alignment issue on iOS 14, but not sure. – Evgeny Karkan May 13 '22 at 17:07
36

Eventually a ViewModifier that embeds the content in a ZStack is more elegant and less code:

public struct PlaceholderStyle: ViewModifier {
    var showPlaceHolder: Bool
    var placeholder: String

    public func body(content: Content) -> some View {
        ZStack(alignment: .leading) {
            if showPlaceHolder {
                Text(placeholder)
                .padding(.horizontal, 15)
            }
            content
            .foregroundColor(Color.white)
            .padding(5.0)            
        }
    }
}

Usage:

TextField("", text: $data)
.modifier(PlaceholderStyle(showPlaceHolder: data.isEmpty,
                           placeholder: "My Placeholder"))
jfk
  • 439
  • 5
  • 5
  • 3
    This should likely be the new accepted answer until SwiftUI has a native way of changing placeholder colour – tyirvine Oct 28 '20 at 05:20
19

It's a bit modification for the @jfk's answer, we can create an extension for view to simplify the modifier code inside the main view and also it can be used for Text and Image.

struct PlaceHolder<T: View>: ViewModifier {
    var placeHolder: T
    var show: Bool
    func body(content: Content) -> some View {
        ZStack(alignment: .leading) {
            if show { placeHolder }
            content
        }
    }
}

extension View {
    func placeHolder<T:View>(_ holder: T, show: Bool) -> some View {
        self.modifier(PlaceHolder(placeHolder:holder, show: show))
    }
}

Usage in TextField:

Add this line of code .placeHolder(Text("Your placeholder"), show: text.isEmpty) as a viewModifier to TextField.

TextField("", text: $text, onEditingChanged: { (changing) in
    print("Changing: \(changing)")
}, onCommit: {
    print("Committed!")
})
    .placeHolder(Text("Your placeholder"), show: text.isEmpty)

Usage in Image:

Further more, as @EmilioPelaez suggested, I modified the code to support placeholder for any view for ex. Image like below.

Image("your_image")
    .placeHolder(Image("placeholder_image"), show: true)
Sateesh Yemireddi
  • 3,976
  • 1
  • 18
  • 34
  • This is a great answer but I think it could be improved some more. Allowing any kind of content instead of requiring `Text` and replacing the `text: String` parameter with `show: Bool` would make it more flexible and it could be used with different kinds of views. – EmilioPelaez Jul 17 '20 at 10:49
  • Ahh.. @EmilioPelaez, that's really good idea to make the place holder available for any view whether it is **Text** or **Image**, I've updated my answer. I stick to the question but your suggestion is simply out of box. Thank you!! – Sateesh Yemireddi Jul 17 '20 at 11:35
1

If you want to preserve the original TextField and you don't mind adding Introspect to your project (https://github.com/siteline/SwiftUI-Introspect), you can do it by accessing the UIKit attributedPlaceholder:

TextField("Email", text: $email)
.introspectTextField { uiTextField in
    uiTextField.attributedPlaceholder = NSAttributedString(string: "placeholder text", 
    attributes: [NSAttributedString.Key.foregroundColor: UIColor.red])
}
Neo
  • 47
  • 4
  • 3
    Doesn't work at all. Library is very poor - there's like 80% chance it will work if all you have is a View and TextField. Once you add styling, or move TextField inside VStack and add styling to VStack - it breaks instantly. – Lars Jun 16 '21 at 20:11
-1

You can try to change the color depending on the content of the TextField. Maybe something like this:

@State var text : String
@State var textFieldColor : Color = .secondary

TextField("Placeholder", text: $text)
   .foregroundColor(textFieldColor)
   .onChange(of: text) { value in
      textFieldColor = value == "" ? .red : .green
   }
Dharman
  • 26,923
  • 21
  • 73
  • 125
fcollf
  • 1