Placeholder in UITextView Placeholder in UITextView ios ios

Placeholder in UITextView


I made a few minor modifications to bcd's solution to allow for initialization from a Xib file, text wrapping, and to maintain background color. Hopefully it will save others the trouble.

UIPlaceHolderTextView.h:

#import <Foundation/Foundation.h>IB_DESIGNABLE@interface UIPlaceHolderTextView : UITextView@property (nonatomic, retain) IBInspectable NSString *placeholder;@property (nonatomic, retain) IBInspectable UIColor *placeholderColor;-(void)textChanged:(NSNotification*)notification;@end

UIPlaceHolderTextView.m:

#import "UIPlaceHolderTextView.h"@interface UIPlaceHolderTextView ()@property (nonatomic, retain) UILabel *placeHolderLabel;@end@implementation UIPlaceHolderTextViewCGFloat const UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION = 0.25;- (void)dealloc{    [[NSNotificationCenter defaultCenter] removeObserver:self];#if __has_feature(objc_arc)#else    [_placeHolderLabel release]; _placeHolderLabel = nil;    [_placeholderColor release]; _placeholderColor = nil;    [_placeholder release]; _placeholder = nil;    [super dealloc];#endif}- (void)awakeFromNib{    [super awakeFromNib];    // Use Interface Builder User Defined Runtime Attributes to set    // placeholder and placeholderColor in Interface Builder.    if (!self.placeholder) {        [self setPlaceholder:@""];    }    if (!self.placeholderColor) {        [self setPlaceholderColor:[UIColor lightGrayColor]];    }    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];}- (id)initWithFrame:(CGRect)frame{    if( (self = [super initWithFrame:frame]) )    {        [self setPlaceholder:@""];        [self setPlaceholderColor:[UIColor lightGrayColor]];        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];    }    return self;}- (void)textChanged:(NSNotification *)notification{    if([[self placeholder] length] == 0)    {        return;    }    [UIView animateWithDuration:UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION animations:^{    if([[self text] length] == 0)    {        [[self viewWithTag:999] setAlpha:1];    }    else    {        [[self viewWithTag:999] setAlpha:0];    }    }];}- (void)setText:(NSString *)text {    [super setText:text];    [self textChanged:nil];}- (void)drawRect:(CGRect)rect{    if( [[self placeholder] length] > 0 )    {        if (_placeHolderLabel == nil )        {            _placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(8,8,self.bounds.size.width - 16,0)];            _placeHolderLabel.lineBreakMode = NSLineBreakByWordWrapping;            _placeHolderLabel.numberOfLines = 0;            _placeHolderLabel.font = self.font;            _placeHolderLabel.backgroundColor = [UIColor clearColor];            _placeHolderLabel.textColor = self.placeholderColor;            _placeHolderLabel.alpha = 0;            _placeHolderLabel.tag = 999;            [self addSubview:_placeHolderLabel];        }        _placeHolderLabel.text = self.placeholder;        [_placeHolderLabel sizeToFit];        [self sendSubviewToBack:_placeHolderLabel];    }    if( [[self text] length] == 0 && [[self placeholder] length] > 0 )    {        [[self viewWithTag:999] setAlpha:1];    }    [super drawRect:rect];}@end


Easy way, just create placeholder text in UITextView by using the following UITextViewDelegate methods:

- (void)textViewDidBeginEditing:(UITextView *)textView{    if ([textView.text isEqualToString:@"placeholder text here..."]) {         textView.text = @"";         textView.textColor = [UIColor blackColor]; //optional    }    [textView becomeFirstResponder];}- (void)textViewDidEndEditing:(UITextView *)textView{    if ([textView.text isEqualToString:@""]) {        textView.text = @"placeholder text here...";        textView.textColor = [UIColor lightGrayColor]; //optional    }    [textView resignFirstResponder];}

just remember to set myUITextView with the exact text on creation e.g.

UITextView *myUITextView = [[UITextView alloc] init];myUITextView.delegate = self;myUITextView.text = @"placeholder text here...";myUITextView.textColor = [UIColor lightGrayColor]; //optional

and make the parent class a UITextViewDelegate before including these methods e.g.

@interface MyClass () <UITextViewDelegate>@end

Code for Swift 3.1

func textViewDidBeginEditing(_ textView: UITextView) {    if (textView.text == "placeholder text here..." && textView.textColor == .lightGray)    {        textView.text = ""        textView.textColor = .black    }    textView.becomeFirstResponder() //Optional}func textViewDidEndEditing(_ textView: UITextView){    if (textView.text == "")    {        textView.text = "placeholder text here..."        textView.textColor = .lightGray    }    textView.resignFirstResponder()}

just remember to set myUITextView with the exact text on creation e.g.

 let myUITextView = UITextView.init() myUITextView.delegate = self myUITextView.text = "placeholder text here..." myUITextView.textColor = .lightGray

and make the parent class a UITextViewDelegate before including these methods e.g.

class MyClass: UITextViewDelegate{}


I wasn't too happy with any of the solutions posted as they were a bit heavy. Adding views to the view isn't really ideal (especially in drawRect:). They both had leaks, which isn't acceptable either.

Here is my solution: SAMTextView

SAMTextView.h

////  SAMTextView.h//  SAMTextView////  Created by Sam Soffes on 8/18/10.//  Copyright 2010-2013 Sam Soffes. All rights reserved.//#import <UIKit/UIKit.h>/** UITextView subclass that adds placeholder support like UITextField has. */@interface SAMTextView : UITextView/** The string that is displayed when there is no other text in the text view. The default value is `nil`. */@property (nonatomic, strong) NSString *placeholder;/** The color of the placeholder. The default is `[UIColor lightGrayColor]`. */@property (nonatomic, strong) UIColor *placeholderTextColor;/** Returns the drawing rectangle for the text views’s placeholder text. @param bounds The bounding rectangle of the receiver. @return The computed drawing rectangle for the placeholder text. */- (CGRect)placeholderRectForBounds:(CGRect)bounds;@end

SAMTextView.m

////  SAMTextView.m//  SAMTextView////  Created by Sam Soffes on 8/18/10.//  Copyright 2010-2013 Sam Soffes. All rights reserved.//#import "SAMTextView.h"@implementation SAMTextView#pragma mark - Accessors@synthesize placeholder = _placeholder;@synthesize placeholderTextColor = _placeholderTextColor;- (void)setText:(NSString *)string {  [super setText:string];  [self setNeedsDisplay];}- (void)insertText:(NSString *)string {  [super insertText:string];  [self setNeedsDisplay];}- (void)setAttributedText:(NSAttributedString *)attributedText {  [super setAttributedText:attributedText];  [self setNeedsDisplay];}- (void)setPlaceholder:(NSString *)string {  if ([string isEqual:_placeholder]) {    return;  }  _placeholder = string;  [self setNeedsDisplay];}- (void)setContentInset:(UIEdgeInsets)contentInset {  [super setContentInset:contentInset];  [self setNeedsDisplay];}- (void)setFont:(UIFont *)font {  [super setFont:font];  [self setNeedsDisplay];}- (void)setTextAlignment:(NSTextAlignment)textAlignment {  [super setTextAlignment:textAlignment];  [self setNeedsDisplay];}#pragma mark - NSObject- (void)dealloc {  [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidChangeNotification object:self];}#pragma mark - UIView- (id)initWithCoder:(NSCoder *)aDecoder {  if ((self = [super initWithCoder:aDecoder])) {    [self initialize];  }  return self;}- (id)initWithFrame:(CGRect)frame {  if ((self = [super initWithFrame:frame])) {    [self initialize];  }  return self;}- (void)drawRect:(CGRect)rect {  [super drawRect:rect];  if (self.text.length == 0 && self.placeholder) {    rect = [self placeholderRectForBounds:self.bounds];    UIFont *font = self.font ? self.font : self.typingAttributes[NSFontAttributeName];    // Draw the text    [self.placeholderTextColor set];    [self.placeholder drawInRect:rect withFont:font lineBreakMode:NSLineBreakByTruncatingTail alignment:self.textAlignment];  }}#pragma mark - Placeholder- (CGRect)placeholderRectForBounds:(CGRect)bounds {  // Inset the rect  CGRect rect = UIEdgeInsetsInsetRect(bounds, self.contentInset);  if (self.typingAttributes) {    NSParagraphStyle *style = self.typingAttributes[NSParagraphStyleAttributeName];    if (style) {      rect.origin.x += style.headIndent;      rect.origin.y += style.firstLineHeadIndent;    }  }  return rect;}#pragma mark - Private- (void)initialize {  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:self];  self.placeholderTextColor = [UIColor colorWithWhite:0.702f alpha:1.0f];}- (void)textChanged:(NSNotification *)notification {  [self setNeedsDisplay];}@end

It's a lot simpler than the others, as it doesn't use subviews (or have leaks). Feel free to use it.

Update 11/10/11: It is now documented and supports use in Interface Builder.

Update 11/24/13: Point to new repo.