ios - Placeholder in UITextView

ID : 2394

viewed : 147

Tags : iosobjective-ccocoa-touchuitextviewplaceholder





Top 5 Answer for ios - Placeholder in UITextView

vote vote

96

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 UIPlaceHolderTextView  CGFloat 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 
vote vote

87

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 {  } 
vote vote

71

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.

vote vote

70

I found myself a very easy way to imitate a place-holder

  1. in the NIB or code set your textView's textColor to lightGrayColor (most of the time)
  2. make sure that your textView's delegate is linked to file's owner and implement UITextViewDelegate in your header file
  3. set the default text of your text view to (example: "Foobar placeholder")
  4. implement: (BOOL) textViewShouldBeginEditing:(UITextView *)textView

Edit:

Changed if statements to compare tags rather than text. If the user deleted their text it was possible to also accidentally delete a portion of the place holder @"Foobar placeholder".This meant if the user re-entered the textView the following delegate method, -(BOOL) textViewShouldBeginEditing:(UITextView *) textView, it would not work as expected. I tried comparing by the color of the text in the if statement but found that light grey color set in interface builder is not the same as light grey color set in code with [UIColor lightGreyColor]

- (BOOL) textViewShouldBeginEditing:(UITextView *)textView {     if(textView.tag == 0) {         textView.text = @"";         textView.textColor = [UIColor blackColor];         textView.tag = 1;     }     return YES; } 

It is also possible to reset the placeholder text when the keyboard returns and the [textView length] == 0

EDIT:

Just to make the last part clearer - here's is how you can set the placeholder text back:

- (void)textViewDidChange:(UITextView *)textView {    if([textView.text length] == 0)    {        textView.text = @"Foobar placeholder";        textView.textColor = [UIColor lightGrayColor];        textView.tag = 0;    } } 
vote vote

60

What you can do is set up the text view with some initial value in the text property, and change the textColor to [UIColor grayColor] or something similar. Then, whenever the text view becomes editable, clear the text and present a cursor, and if the text field is ever empty again, put your placeholder text back. Change the color to [UIColor blackColor] as appropriate.

It's not exactly the same as the placeholder functionality in a UITextField, but it's close.

Top 3 video Explaining ios - Placeholder in UITextView







Related QUESTION?