121

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
  • 4,203
  • 3
  • 15
  • 29

9 Answers9

225

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
  • 95,414
  • 31
  • 268
  • 278
  • 6
    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
  • set alignment to .topLeading for multiline text fields with reserved space. – tomu94 Aug 30 '23 at 02:12
43

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
  • 517
  • 5
  • 5
35

For iOS 16.0 and above

TextField("", text: $viewModel.userName, prompt: Text("Phone, email or username").foregroundColor(.gray))
Anton
  • 726
  • 11
  • 23
23

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
  • 4,289
  • 1
  • 20
  • 37
  • 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
16

On iOS 15, you can use prompt

public init(text: Binding<String>, prompt: Text? = nil, @ViewBuilder label: () -> Label)

Which works as a placeholder view

Abhishek Thapliyal
  • 3,497
  • 6
  • 30
  • 69
15

Super easy solution

We can substitute TextField's placeholder with another Text's string.

So, we can control a color of a fake "placeholder".

struct ContentView: View {
    
    @State private var text: String = ""
    
    var body: some View {
        ZStack(alignment: .leading) {
            if text.isEmpty {
                Text("Type Here")
                    .foregroundColor(.red.opacity(0.4))
                    .font(.system(size: 50))
            }
            TextField("", text: $text)
                .font(.system(size: 50))
        }
        .padding(20)
    }
}

enter image description here

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
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
  • 5
    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
TextField("", text: $text, prompt: Text("Hello").foregroundColor(.white))

change text color in prompt

soundflix
  • 928
  • 9
  • 22
0

Changing the Placeholder text color of a TextField:

https://medium.com/app-makers/how-to-use-textfield-in-swiftui-2fc0ca00f75b

struct SuperTextField: View {

var placeholder: Text
@Binding var text: String
var editingChanged: (Bool)->() = { _ in }
var commit: ()->() = { }

var body: some View {
    ZStack(alignment: .leading) {
        if text.isEmpty { placeholder }
        TextField("", text: $text, onEditingChanged: editingChanged, onCommit: commit)
    }
}   }

and usage

@State var text: String = "TextField Text"

var body: some View {
    SuperTextField(
        placeholder: Text("Placeholder Text").foregroundColor(.red),
        text: $text
    )
}
Akhtar
  • 3,172
  • 5
  • 19
  • 21