How to Change UITextField Placeholder Color in Swift

How to Change UITextField Placeholder Color in Swift

Written by

in

Quick answer: To change the placeholder text color of a UITextField in Swift, assign an NSAttributedString to its attributedPlaceholder property with the NSAttributedString.Key.foregroundColor attribute set to your desired UIColor. You can also write a clean UITextField extension to set placeholder text and color with a single helper method.

Why this happens

By default, UITextField renders placeholder text using a system-defined light or dark gray color (UIColor.placeholderText). Unlike properties such as textColor, font, or backgroundColor, UIKit does not expose a dedicated placeholderColor property on UITextField.

When you implement a custom user interface—such as a dark navy, black, or branded background on your input fields—the default gray placeholder blends into the background and becomes unreadable. Because UITextField.placeholder is a plain String?, changing its color requires interacting with formatted text APIs.

Historical workarounds that attempted to access internal subviews using Key-Value Coding (KVC), such as setting placeholderLabel.textColor in code or Interface Builder User Defined Runtime Attributes, cause fatal runtime exceptions on modern iOS versions. The official, supported approach is to supply a formatted attributed string to attributedPlaceholder.

How to fix it

1. Set the attributedPlaceholder Property Directly

The standard, Apple-approved way to modify placeholder color is setting the attributedPlaceholder property using NSAttributedString. Pass your desired UIColor under the .foregroundColor key.

import UIKit

class LoginViewController: UIViewController {
    @IBOutlet weak var emailTextField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        emailTextField.backgroundColor = UIColor(red: 0.08, green: 0.18, blue: 0.36, alpha: 1.0)
        emailTextField.textColor = .white

        let placeholderText = "Enter your email address"
        let attributes: [NSAttributedString.Key: Any] = [
            .foregroundColor: UIColor.lightGray,
            .font: emailTextField.font ?? UIFont.systemFont(ofSize: 14)
        ]

        emailTextField.attributedPlaceholder = NSAttributedString(
            string: placeholderText,
            attributes: attributes
        )
    }
}

This method keeps your layout fully compliant with App Store review guidelines and guarantees backward and forward compatibility.

2. Create a Reusable UITextField Extension

Configuring NSAttributedString manually on every text field introduces repetitive boilerplate. You can streamline this across your project with an extension on UITextField.

Create a new Swift file named UITextField+PlaceholderColor.swift:

import UIKit

extension UITextField {
    /// Sets the placeholder text along with a custom text color.
    func setPlaceholder(_ text: String, color: UIColor) {
        let attributes: [NSAttributedString.Key: Any] = [
            .foregroundColor: color,
            .font: self.font ?? UIFont.systemFont(ofSize: UIFont.systemFontSize)
        ]
        self.attributedPlaceholder = NSAttributedString(string: text, attributes: attributes)
    }

    /// Updates only the color of the existing placeholder, retaining current text.
    func setPlaceholderColor(_ color: UIColor) {
        guard let currentPlaceholder = self.placeholder, !currentPlaceholder.isEmpty else {
            return
        }
        setPlaceholder(currentPlaceholder, color: color)
    }
}

Use the extension anywhere in your view controllers:

override func viewDidLoad() {
    super.viewDidLoad()

    usernameTextField.setPlaceholder("Username", color: .systemTeal)
    passwordTextField.setPlaceholderColor(.secondaryLabel)
}

3. Support Interface Builder with an @IBInspectable Extension

If you build interfaces primarily in Storyboards or XIB files, you can expose a custom placeholder color picker in the Xcode Attributes Inspector using an @IBInspectable property.

import UIKit

extension UITextField {
    @IBInspectable var placeholderColor: UIColor? {
        get {
            guard let attributedPlaceholder = self.attributedPlaceholder,
                  attributedPlaceholder.length > 0 else {
                return nil
            }
            let attributes = attributedPlaceholder.attributes(at: 0, effectiveRange: nil)
            return attributes[.foregroundColor] as? UIColor
        }
        set {
            guard let newColor = newValue else { return }
            let currentText = self.placeholder ?? ""
            let currentFont = self.font ?? UIFont.systemFont(ofSize: UIFont.systemFontSize)

            self.attributedPlaceholder = NSAttributedString(
                string: currentText,
                attributes: [
                    .foregroundColor: newColor,
                    .font: currentFont
                ]
            )
        }
    }
}

After adding this extension:
1. Open your Storyboard or XIB file.
2. Select your UITextField.
3. Open the Attributes Inspector in the right sidebar.
4. Locate the Placeholder Color field at the top of the inspector panel and select your desired color.

4. Support Dark Mode Using Dynamic System Colors

When styling text fields for apps that support both Light and Dark modes, avoid hardcoded colors like UIColor.white or UIColor.gray. Instead, supply semantic system colors or custom asset catalog color sets initialized with UIColor(named:).

let dynamicColor = UIColor { traitCollection in
    switch traitCollection.userInterfaceStyle {
    case .dark:
        return UIColor.systemGray2
    default:
        return UIColor.systemGray4
    }
}

searchTextField.setPlaceholder("Search documents...", color: dynamicColor)

When the system switches themes, UITextField automatically re-evaluates dynamic colors without requiring manual view updates.

5. Why You Must Avoid Key-Value Coding (KVC) Hacks

Older tutorials recommend setting _placeholderLabel.textColor or placeholderLabel.textColor using runtime attributes or code:

// DO NOT USE: Crashes on iOS 13 and later
textField.setValue(UIColor.white, forKeyPath: "placeholderLabel.textColor")

Starting with iOS 13, Apple prohibited accessing private ivars and view properties using KVC on standard UIKit controls. Running this code triggers a fatal NSUnknownKeyException terminating your application with the message:

Thread 1: Fatal error: This class is not key value coding-compliant for the key placeholderLabel.

Always use attributedPlaceholder instead of private key paths.

How to prevent it

  • Set attributes after text: Always configure the placeholder text before or simultaneously with the color attribute; setting textField.placeholder = "New Text" later overwrites attributedPlaceholder and resets the color back to the default gray.
  • Avoid KVC paths: Never use setValue(_:forKeyPath:) for internal UIKit view hierarchies like placeholderLabel.
  • Use dynamic colors: Use asset catalog colors or semantic system colors (UIColor.secondaryLabel, UIColor.tertiaryLabel) to ensure readable contrast in both Light and Dark appearances.
  • Standardize via subclasses or extensions: Keep all text field styling logic in shared extensions or custom UITextField subclasses to avoid scattered manual attribute setups.

FAQ

Does changing the font size reset the placeholder text color?

If you change the text field’s font after assigning attributedPlaceholder, UIKit may not automatically scale the attributed string’s font. Reassign attributedPlaceholder with the updated font and color, or apply the custom font size directly inside the attributes dictionary.

Why did my custom placeholder color revert to gray after I updated the text?

Assigning a new value directly to textField.placeholder clears the existing attributedPlaceholder property. If you need to change the placeholder text dynamically at runtime, assign a new NSAttributedString to attributedPlaceholder rather than updating placeholder.

How do I change the placeholder color of a SwiftUI TextField?

In SwiftUI, you can customize placeholder appearance using the prompt parameter with a styled Text view, or by applying the .foregroundStyle modifier to a Text view inside a custom ZStack overlay.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.