For a recent iPhone app I wanted a multi-line editable text field, in which I could display placeholder text so my user would know what to do with the field.
Placeholder text is a common affordance provided by several UI classes such as UITextField. But UITextView in iOS 4 does not support placeholder text.
A web search turned up several implementations. Some of these seemed too complex, and some failed to provide the behavior I wanted:
- If the view is empty, show the placeholder.
- Whenever the view is the first responder, hide the placeholder.
Here's my implementation. It's very basic. For example it doesn't let you customize the color of the placeholder text; and the placeholder is always centered vertically within the bounds of the text view. But it does provide the behaviors listed above.
PlaceholderTextView.h
#import <UIKit/UIKit.h>
@interface PlaceholderTextView : UITextView {
UILabel *placeholderLabel;
NSString *placeholderText;
}
@property (nonatomic, retain) NSString *placeholderText;
@end
PlaceholderTextView.m
#import "PlaceholderTextView.h"
@implementation PlaceholderTextView
@synthesize placeholderText;
- (void)hidePlaceholder {
if ([placeholderLabel superview]) {
[placeholderLabel removeFromSuperview];
}
}
- (void)maybeShowPlaceholder {
if ([placeholderText length] > 0) {
if (![self hasText] && ![placeholderLabel superview]) {
CGRect frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
placeholderLabel.frame = frame;
placeholderLabel.text = placeholderText;
[self addSubview:placeholderLabel];
}
}
}
- (void)initCommon {
if (!placeholderLabel) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(beganEditing:) name:UITextViewTextDidBeginEditingNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(endedEditing:) name:UITextViewTextDidEndEditingNotification object:nil];
placeholderLabel = [[UILabel alloc] init];
placeholderLabel.numberOfLines = 0;
placeholderLabel.backgroundColor = [UIColor clearColor];
placeholderLabel.textColor = [UIColor lightGrayColor];
placeholderLabel.font = self.font;
placeholderLabel.lineBreakMode = UILineBreakModeWordWrap;
placeholderLabel.textAlignment = UITextAlignmentCenter;
[self maybeShowPlaceholder];
}
}
- (void)awakeFromNib {
[super awakeFromNib];
[self initCommon];
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initCommon];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self initCommon];
}
return self;
}
- (id)init {
self = [super init];
if (self) {
[self initCommon];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[placeholderLabel release];
[placeholderText release];
[super dealloc];
}
- (void)setPlaceholderText:(NSString *)newValue {
[newValue retain];
NSString *oldValue = placeholderText;
placeholderText = newValue;
[oldValue release];
[self maybeShowPlaceholder];
}
- (void)beganEditing:(NSNotification *)notification {
[self hidePlaceholder];
}
- (void)endedEditing:(NSNotification *)notification {
[self maybeShowPlaceholder];
}
@end