ChatGPT解决这个技术问题 Extra ChatGPT

iPhone UITextField - Change placeholder text color

I'd like to change the color of the placeholder text I set in my UITextField controls, to make it black.

I'd prefer to do this without using normal text as the placeholder and having to override all the methods to imitate the behaviour of a placeholder.

I believe if I override this method:

- (void)drawPlaceholderInRect:(CGRect)rect

then I should be able to do this. But I'm unsure how to access the actual placeholder object from within this method.


u
user1071136

Since the introduction of attributed strings in UIViews in iOS 6, it's possible to assign a color to the placeholder text like this:

if ([textField respondsToSelector:@selector(setAttributedPlaceholder:)]) {
  UIColor *color = [UIColor blackColor];
  textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:placeholderText attributes:@{NSForegroundColorAttributeName: color}];
} else {
  NSLog(@"Cannot set placeholder text's color, because deployment target is earlier than iOS 6.0");
  // TODO: Add fall-back code to set placeholder color.
}

This is good - but remember you need to set a placeholder value in IB before this will work
its also worth wrapping this in a respondsToSelector call - as without it this code will crash on pre 6.0 deployment target ( unrecognized selector sent to instance)
The docs for the attributedPlaceholder says that is uses text attributes, except for colour.
Any idea why it's not working for the attribute - NSFontAttributeName [[NSAttributedString alloc] initWithString:@"placeholder" attributes: @{NSForegroundColorAttributeName: color, NSFontAttributeName : font}];
On iOS 7 I got this error: [<UITextField 0x11561d90> valueForUndefinedKey:]: this class is not key value coding-compliant for the key _field.
J
Jack

Easy and pain-free, could be an easy alternative for some.

_placeholderLabel.textColor

Not suggested for production, Apple may reject your submission.

https://i.stack.imgur.com/EPcOv.png


Are you using this in production? I mean to access a private property.
Maybe add the text to your answer for fast copy and paste. _placeholderLabel.textColor
Don't use this method, i have used this and itune store, has rejected my application.
I think any kind of private library method should NEVER be recommended as solution to anything
@jungledev _placeholderLabel is a private property. This solution is subject to being rejected for use of private API.
K
Kuldeep

You can override drawPlaceholderInRect:(CGRect)rect as such to manually render the placeholder text:

- (void) drawPlaceholderInRect:(CGRect)rect {
    [[UIColor blueColor] setFill];
    [[self placeholder] drawInRect:rect withFont:[UIFont systemFontOfSize:16]];
}

Something like [self.placeholder drawInRect:rect withFont:self.font lineBreakMode:UILineBreakModeTailTruncation alignment:self.textAlignment]; is probably better. That way you will respect the textAlignment property. I have added this to my SSTextField class. Feel free to use in your projects.
Absolutely do NOT do what Koteg said. NEVER override methods through categories. EVER
@JoshuaWeinberg Is there any specific reason behind your sugestion.
@Krishnan implementing a duplicate method in a category is not supported, and you can never be certain which method will be called, or if both with be called, or the order in which they will be called.
In iOS7 you can alter the rect by using CGRectInset(rect, 0, (rect.size.height - self.font.lineHeight) / 2.0) to vertically center the text.
M
Manju

You can Change the Placeholder textcolor to any color which you want by using the below code.

UIColor *color = [UIColor lightTextColor];
YOURTEXTFIELD.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"PlaceHolder Text" attributes:@{NSForegroundColorAttributeName: color}];

Best answer suggested. Works, and uses documented APIs.
How do you make this work for UISearchBar? There is no attributedPlaceholder property.
This answer is not correct – according to the docs, the text color information of attributedPlaceholder is ignored developer.apple.com/library/ios/documentation/UIKit/Reference/…
Thanks everyone. Hope it was of some help. Adlai Holler give it a try bro!!! This answer was for the previous version of iOS. If you have better answer we are open to suggestions.
Doesn't work on iOS8 as there's no attributedPlaceholder property
e
ekscrypto

This works in Swift <3.0:

myTextField.attributedPlaceholder = 
NSAttributedString(string: "placeholder text", attributes: [NSForegroundColorAttributeName : UIColor.redColor()])

Tested in iOS 8.2 and iOS 8.3 beta 4.

Swift 3:

myTextfield.attributedPlaceholder =
NSAttributedString(string: "placeholder text", attributes: [NSForegroundColorAttributeName : UIColor.red])

Swift 4:

myTextfield.attributedPlaceholder =
NSAttributedString(string: "placeholder text", attributes: [NSAttributedStringKey.foregroundColor: UIColor.red])

Swift 4.2:

myTextfield.attributedPlaceholder =
NSAttributedString(string: "placeholder text", attributes: [NSAttributedString.Key.foregroundColor: UIColor.red])

Using your solution on iOS8.2, works like a charm. Perfect solution here. Using in objective C also.
d
digdog

Maybe you want to try this way, but Apple might warn you about accessing private ivar:

[self.myTextField setValue:[UIColor darkGrayColor] 
                forKeyPath:@"_placeholderLabel.textColor"];

NOTE This is not working on iOS 7 anymore, according to Martin Alléus.


I use this method in my app. The review was OK. So I think it's fine to use it.
This is not app store safe and should not be encouraged, you're not guaranteed to get approved or stay in approved with these techniques.
It shouldn't really matter if it's approved or not really. The important thing is that this could break your app in future OS updates.
Not having any problem with iOS 7... I gave it a try despite the note and it seems to work fine and I've used this approach in the past with no problems.
This was always a bad idea, and now it's broken on iOS 13: Access to UITextField's _placeholderLabel ivar is prohibited. This is an application bug 😈
D
DZoki019

Swift 3.0 + Storyboard

In order to change placeholder color in storyboard, create an extension with next code. (feel free to update this code, if you think, it can be clearer and safer).

extension UITextField {
    @IBInspectable var placeholderColor: UIColor {
        get {
            guard let currentAttributedPlaceholderColor = attributedPlaceholder?.attribute(NSForegroundColorAttributeName, at: 0, effectiveRange: nil) as? UIColor else { return UIColor.clear }
            return currentAttributedPlaceholderColor
        }
        set {
            guard let currentAttributedString = attributedPlaceholder else { return }
            let attributes = [NSForegroundColorAttributeName : newValue]

            attributedPlaceholder = NSAttributedString(string: currentAttributedString.string, attributes: attributes)
        }
    }
}

https://i.stack.imgur.com/Pk85C.png

Swift 4 version

extension UITextField {
    @IBInspectable var placeholderColor: UIColor {
        get {
            return attributedPlaceholder?.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? UIColor ?? .clear
        }
        set {
            guard let attributedPlaceholder = attributedPlaceholder else { return }
            let attributes: [NSAttributedStringKey: UIColor] = [.foregroundColor: newValue]
            self.attributedPlaceholder = NSAttributedString(string: attributedPlaceholder.string, attributes: attributes)
        }
    }
}

Swift 5 version

extension UITextField {
    @IBInspectable var placeholderColor: UIColor {
        get {
            return attributedPlaceholder?.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? UIColor ?? .clear
        }
        set {
            guard let attributedPlaceholder = attributedPlaceholder else { return }
            let attributes: [NSAttributedString.Key: UIColor] = [.foregroundColor: newValue]
            self.attributedPlaceholder = NSAttributedString(string: attributedPlaceholder.string, attributes: attributes)
        }
    }
}

The compiler can't find out NSAttributedStringKey.
Works in iOS 13
c
clemens

In Swift:

if let placeholder = yourTextField.placeholder {
    yourTextField.attributedPlaceholder = NSAttributedString(string:placeholder, 
        attributes: [NSForegroundColorAttributeName: UIColor.blackColor()])
}

In Swift 4.0:

if let placeholder = yourTextField.placeholder {
    yourTextField.attributedPlaceholder = NSAttributedString(string:placeholder, 
        attributes: [NSAttributedStringKey.foregroundColor: UIColor.black])
}

If you didn't set the placeholder text before calling that it will crash the app
This is the best one, thank you. @apinho nothing will crash here
//In Swift 4 if let placeholder = yourTextField.placeholder { yourTextField.attributedPlaceholder = NSAttributedString(string:placeholder, attributes: [NSAttributedStringKey.foregroundColor: UIColor.white]) }
Beware that changing the placeholder text value after this code has been executed will bring back the default placeholder color.
F
Fattie

The following only with iOS6+ (as indicated in Alexander W's comment):

UIColor *color = [UIColor grayColor];
nameText.attributedPlaceholder =
   [[NSAttributedString alloc]
       initWithString:@"Full Name"
       attributes:@{NSForegroundColorAttributeName:color}];

A
Ashu

I had already faced this issue. In my case below code is correct.

Objective C

[textField setValue:[UIColor whiteColor] forKeyPath:@"_placeholderLabel.textColor"];

For Swift 4.X

tf_mobile.setValue(UIColor.white, forKeyPath: "_placeholderLabel.textColor")

For iOS 13 Swift Code

tf_mobile.attributedPlaceholder = NSAttributedString(string:"PlaceHolder Text", attributes: [NSAttributedString.Key.foregroundColor: UIColor.red])

You can also use below code for iOS 13

let iVar = class_getInstanceVariable(UITextField.self, "_placeholderLabel")!
let placeholderLabel = object_getIvar(tf_mobile, iVar) as! UILabel
placeholderLabel.textColor = .red

Hope, this may help you.


@Ashu this causes crash in ios 13 'Access to UITextField's _placeholderLabel ivar is prohibited. This is an application bug' it wont be even build I am not talking about placing into AppStore
IOS 13 this was restricted can't use this anymore
@Mehdico I updated the answer for iOS 13. Hope this will helps you.
Took me far too long to learn that I had to make this change in viewWillAppear or viewDidAppear. viewDidLoad is too early.
I had to use UIColor(displayP3Red: 255, green: 255, blue: 255, alpha: 0.3) constructor for UIColor instead of UIColor(red: 255, green: 255, blue: 255, alpha: 0.3)
Y
Yogi

With this we can change the color of textfield's placeholder text in iOS

[self.userNameTxt setValue:[UIColor colorWithRed:41.0/255.0 green:91.0/255.0 blue:106.0/255.0 alpha:1.0] forKeyPath:@"_placeholderLabel.textColor"];

@Teddy how did you do it now? I started to have problems with this.
@Jalil I hope I am still on time for you. textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"text" attributes:@{NSForegroundColorAttributeName:[UIColor colorWithHexString:@"ffffff55"]}];
J
Joshua Wolff

in swift 3.X

textField.attributedPlaceholder = NSAttributedString(string: "placeholder text", attributes:[NSForegroundColorAttributeName: UIColor.black])

in swift 5

textField.attributedPlaceholder = NSAttributedString(string: "placeholder text", attributes: [NSAttributedString.Key.foregroundColor : UIColor.black])

M
Matthias

Why don't you just use UIAppearance method:

[[UILabel appearanceWhenContainedIn:[UITextField class], nil] setTextColor:[UIColor whateverColorYouNeed]];

Works great! Color of text doesn't affected (At least with different proxy for textColor property)
Great worked but showing warning for this method
P
Petr Syrov

Also in your storyboard, without single line of code

https://i.stack.imgur.com/HqS2O.png


How come you know this? It is best approach i think but how one can know this? Please tell i am new to ios programming.
It's very universal. Once you know it use it everywhere ;)
I know once I understand whats running behind it i am going to use it a lot. But I mean "_placeholderLabel.textColor" this should be a child view of textfield. Is there a way to see this type of information about a control?
@Yawar You can use the view hierarchy inspector in Xcode, or introspect the view in the debugger.
good one, regardless of textfield, you can use the same 'tile/name' for keypath
F
Fahim Parkar

For iOS 6.0 +

[textfield setValue:your_color forKeyPath:@"_placeholderLabel.textColor"];

Hope it helps.

Note: Apple may reject (0.01% chances) your app as we are accessing private API. I am using this in all my projects since two years, but Apple didn't ask for this.


throws a terminating with uncaught exception of type NSException for iOS8
M
ManuQiao

For Xamarin.iOS developers, I found it from this document https://developer.xamarin.com/api/type/Foundation.NSAttributedString/

textField.AttributedPlaceholder = new NSAttributedString ("Hello, world",new UIStringAttributes () { ForegroundColor =  UIColor.Red });

Thank you !! I was first using CTStringAttributes and not UIStringAttributes and couldn't figure it out. Be careful to not use this : new NSAttributedString("placeholderstring", new CTStringAttributes() { ForegroundColor = UIColor.Blue.CGColor });
M
Machado

Swift version. Probably it would help someone.

class TextField: UITextField {
   override var placeholder: String? {
        didSet {
            let placeholderString = NSAttributedString(string: placeholder!, attributes: [NSForegroundColorAttributeName: UIColor.whiteColor()])
            self.attributedPlaceholder = placeholderString
        }
    }
}

K
Kuldeep

iOS 6 and later offers attributedPlaceholder on UITextField. iOS 3.2 and later offers setAttributes:range: on NSMutableAttributedString.

You can do the following:

NSMutableAttributedString *ms = [[NSMutableAttributedString alloc] initWithString:self.yourInput.placeholder];
UIFont *placeholderFont = self.yourInput.font;
NSRange fullRange = NSMakeRange(0, ms.length);
NSDictionary *newProps = @{NSForegroundColorAttributeName:[UIColor yourColor], NSFontAttributeName:placeholderFont};
[ms setAttributes:newProps range:fullRange];
self.yourInput.attributedPlaceholder = ms;

Not sure what is causing the problem, this code i have called in viewdidLoad. new color and font size appears only after redraw. any thing else needs to be done along with this?
it got solved, i forgot to set font for UITextfield before using that font for placeholder text. my bad
K
Kuldeep

To handle both vertical and horizontal alignment as well as color of placeholder in iOS7. drawInRect and drawAtPoint no longer use current context fillColor.

https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/CustomTextProcessing/CustomTextProcessing.html

Obj-C

@interface CustomPlaceHolderTextColorTextField : UITextField

@end


@implementation CustomPlaceHolderTextColorTextField : UITextField


-(void) drawPlaceholderInRect:(CGRect)rect  {
    if (self.placeholder) {
        // color of placeholder text
        UIColor *placeHolderTextColor = [UIColor redColor];

        CGSize drawSize = [self.placeholder sizeWithAttributes:[NSDictionary dictionaryWithObject:self.font forKey:NSFontAttributeName]];
        CGRect drawRect = rect;

        // verticially align text
        drawRect.origin.y = (rect.size.height - drawSize.height) * 0.5;

        // set alignment
        NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
        paragraphStyle.alignment = self.textAlignment;

        // dictionary of attributes, font, paragraphstyle, and color
        NSDictionary *drawAttributes = @{NSFontAttributeName: self.font,
                                     NSParagraphStyleAttributeName : paragraphStyle,
                                     NSForegroundColorAttributeName : placeHolderTextColor};


        // draw
        [self.placeholder drawInRect:drawRect withAttributes:drawAttributes];
    }
}

@end

Thanks for this excellent solution-- which properly centers text vertically (useful with custom fonts). The only thing I would add is that this solution is not compatible with iOS 6 or older (easy enough to fix by falling back on [self.placeholder drawInRect:rect withFont:self.font lineBreakMode:NSLineBreakByTruncatingTail alignment:self.textAlignment];
M
Mantosh Kumar

This solution for Swift 4.1

    textName.attributedPlaceholder = NSAttributedString(string: textName.placeholder!, attributes: [NSAttributedStringKey.foregroundColor : UIColor.red])

o
osxdirk

Categories FTW. Could be optimized to check for effective color change.

#import <UIKit/UIKit.h>

@interface UITextField (OPConvenience)

@property (strong, nonatomic) UIColor* placeholderColor;

@end

#import "UITextField+OPConvenience.h"

@implementation UITextField (OPConvenience)

- (void) setPlaceholderColor: (UIColor*) color {
    if (color) {
        NSMutableAttributedString* attrString = [self.attributedPlaceholder mutableCopy];
        [attrString setAttributes: @{NSForegroundColorAttributeName: color} range: NSMakeRange(0,  attrString.length)];
        self.attributedPlaceholder =  attrString;
    }
}

- (UIColor*) placeholderColor {
    return [self.attributedPlaceholder attribute: NSForegroundColorAttributeName atIndex: 0 effectiveRange: NULL];
}

@end

F
Fattie

Swift 5 WITH CAVEAT.

let attributes = [ NSAttributedString.Key.foregroundColor: UIColor.someColor ]
let placeHolderString = NSAttributedString(string: "DON'T_DELETE", attributes: attributes)
txtField.attributedPlaceholder = placeHolderString

The caveat being that you MUST enter a non-empty String where "DON'T_DELETE" is, even if that string is set in code elsewhere. Might save you five minutes of head-sctratching.

if subclassing you MUST do it in layoutSubviews (not in init)

strangely you do NOT have to clear the normal placeholder. it knows to not draw placeholder if you're using the attributed placeholder.


C
Community

Overriding drawPlaceholderInRect: would be the correct way, but it does not work due to a bug in the API (or the documentation).

The method never gets called on an UITextField.

See also drawTextInRect on UITextField not called

You might use digdog's solution. As I am not sure if that gets past Apples review, I chose a different solution: Overlay the text field with my own label which imitates the placeholder behaviour.

This is a bit messy though. The code looks like this (Note I am doing this inside a subclass of TextField):

@implementation PlaceholderChangingTextField

- (void) changePlaceholderColor:(UIColor*)color
{    
    // Need to place the overlay placeholder exactly above the original placeholder
    UILabel *overlayPlaceholderLabel = [[[UILabel alloc] initWithFrame:CGRectMake(self.frame.origin.x + 8, self.frame.origin.y + 4, self.frame.size.width - 16, self.frame.size.height - 8)] autorelease];
    overlayPlaceholderLabel.backgroundColor = [UIColor whiteColor];
    overlayPlaceholderLabel.opaque = YES;
    overlayPlaceholderLabel.text = self.placeholder;
    overlayPlaceholderLabel.textColor = color;
    overlayPlaceholderLabel.font = self.font;
    // Need to add it to the superview, as otherwise we cannot overlay the buildin text label.
    [self.superview addSubview:overlayPlaceholderLabel];
    self.placeholder = nil;
}

how would you feel about sharing your review-friendly solution?
I added the solution I used. I had to do a bit of digging, as this was some time ago :)
I did something similar to this, but I placed the code in a category, and needed to do a check in shouldChangeCharacters on whether to make it visible, which was a second method in the category called - (void) overlayPlaceholderVisible : (BOOL) visible;
C
Community

Iam new to xcode and i found a way around to the same effect.

I placed a uilabel in place of place holder with the desired format and hide it in

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    switch (textField.tag)
    {
        case 0:
            lblUserName.hidden=YES;
            break;

        case 1:
            lblPassword.hidden=YES;
            break;
 
        default:
            break;
    }
}

I agree its a work around and not a real solution but the effect was same got it from this link

NOTE: Still works on iOS 7 :|


A
Aleksei Minaev

The best i can do for both iOS7 and less is:

- (CGRect)placeholderRectForBounds:(CGRect)bounds {
  return [self textRectForBounds:bounds];
}

- (CGRect)editingRectForBounds:(CGRect)bounds {
  return [self textRectForBounds:bounds];
}

- (CGRect)textRectForBounds:(CGRect)bounds {
  CGRect rect = CGRectInset(bounds, 0, 6); //TODO: can be improved by comparing font size versus bounds.size.height
  return rect;
}

- (void)drawPlaceholderInRect:(CGRect)rect {
  UIColor *color =RGBColor(65, 65, 65);
  if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) {
    [self.placeholder drawInRect:rect withAttributes:@{NSFontAttributeName:self.font, UITextAttributeTextColor:color}];
  } else {
    [color setFill];
    [self.placeholder drawInRect:rect withFont:self.font];
  }
}

W
Wolfgang Schreurs

For those using Monotouch (Xamarin.iOS), here's Adam's answer, translated to C#:

public class MyTextBox : UITextField
{
    public override void DrawPlaceholder(RectangleF rect)
    {
        UIColor.FromWhiteAlpha(0.5f, 1f).SetFill();
        new NSString(this.Placeholder).DrawString(rect, Font);
    }
}

Great. This was not really obvious, you probably saved me some time :) I did edit your solution though, since I think it's a better idea to use the font set in the Font property of the text field.
B
Badal Shah

For set Attributed Textfield Placeholder with Multiple color ,

Just specify the Text ,

  //txtServiceText is your Textfield
 _txtServiceText.placeholder=@"Badal/ Shah";
    NSMutableAttributedString *mutable = [[NSMutableAttributedString alloc] initWithString:_txtServiceText.placeholder];
     [mutable addAttribute: NSForegroundColorAttributeName value:[UIColor whiteColor] range:[_txtServiceText.placeholder rangeOfString:@"Badal/"]]; //Replace it with your first color Text
    [mutable addAttribute: NSForegroundColorAttributeName value:[UIColor orangeColor] range:[_txtServiceText.placeholder rangeOfString:@"Shah"]]; // Replace it with your secondcolor string.
    _txtServiceText.attributedPlaceholder=mutable;

Output :-

https://i.stack.imgur.com/msxt8.png


T
Tomer Even

I needed to keep the placeholder alignment so adam's answer was not enough for me.

To solve this I used a small variation that I hope will help some of you too:

- (void) drawPlaceholderInRect:(CGRect)rect {
    //search field placeholder color
    UIColor* color = [UIColor whiteColor];

    [color setFill];
    [self.placeholder drawInRect:rect withFont:self.font lineBreakMode:UILineBreakModeTailTruncation alignment:self.textAlignment];
}

UILineBreakModeTailTruncation is deprecated as of iOS 6.
M
MaxEcho
[txt_field setValue:ColorFromHEX(@"#525252") forKeyPath:@"_placeholderLabel.textColor"];

A little hint on this is that you are accessing a private iVar (_placeholderLabel) and in the past Apple has been a little fincky about doing that. :)
k
kolinko

Another option that doesn't require subclassing - leave placeholder blank, and put a label on top of edit button. Manage the label just like you would manage the placeholder (clearing once user inputs anything..)


I think that this would be efficient if you have one textfield, but if you're trying to make this change on a more global scale this solution would add a considerable amount of overhead to your project.