SwiftUI. How to change the placeholder color of the TextField?
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?
Solution 1:
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)
}
💡 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:
✅ 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 😉
Solution 2:
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"))
Solution 3:
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)
Solution 4:
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])
}