如何更改 TextField 的占位符颜色?

我想更改 TextField 的占位符颜色,但找不到相应的方法。

我尝试设置 foregroundColoraccentColor,但它不改变占位符的颜色。

密码如下:

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

也许还没有这方面的 API?

41162 次浏览

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 😉

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"))

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)

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])
}

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

On iOS 15, you can use prompt

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

Which works as a placeholder view

For iOS 16.0 and above

TextField("", text: $viewModel.userName, prompt: Text("Phone, email or username").foregroundColor(.gray))