UITextView默認(rèn)是不帶有占位文字的,如果實(shí)現(xiàn)這個(gè)功能,大致有兩個(gè)方法:
- textView中添加一個(gè)label,讓label實(shí)現(xiàn)占位文字功能.
- 重寫drawRect方法,直接將文字畫上去.
IQKeyboradManager中實(shí)現(xiàn)這個(gè)功能用的是第一種方法,實(shí)現(xiàn)的類為IQTextView,繼承自UITextView.
方法一:
IQTextView.h
@interface IQTextView : UITextView
/**
Set textView's placeholder text. Default is nil.
*/
@property(nullable, nonatomic,copy) IBInspectable NSString *placeholder;
@end
IQTextView.m
@interface IQTextView ()
-(void)refreshPlaceholder;
@end
// 寫到這里比寫成屬性懶加載好,因?yàn)橹挥性O(shè)置占位文字的時(shí)候才需要?jiǎng)?chuàng)建占位Label
@implementation IQTextView
{
UILabel *placeHolderLabel;
}
@synthesize placeholder = _placeholder;
// 初始化方法中添加監(jiān)聽 UITextViewTextDidChangeNotification 事件
-(void)initialize
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshPlaceholder) name:UITextViewTextDidChangeNotification object:self];
}
-(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (instancetype)init
{
self = [super init];
if (self) {
[self initialize];
}
return self;
}
// xib加載時(shí)也要初始化
-(void)awakeFromNib
{
[super awakeFromNib];
[self initialize];
}
-(void)refreshPlaceholder
{
if([[self text] length])
{
[placeHolderLabel setAlpha:0];
}
else
{
[placeHolderLabel setAlpha:1];
}
[self setNeedsLayout]; // 添加刷新標(biāo)記
[self layoutIfNeeded]; // 立即刷新
}
- (void)setText:(NSString *)text
{
[super setText:text];
[self refreshPlaceholder];
}
-(void)setFont:(UIFont *)font
{
[super setFont:font];
placeHolderLabel.font = self.font;
[self setNeedsLayout]; // 添加刷新標(biāo)記
[self layoutIfNeeded]; // 立即刷新
}
-(void)layoutSubviews
{
[super layoutSubviews];
[placeHolderLabel sizeToFit];
placeHolderLabel.frame = CGRectMake(8, 8, CGRectGetWidth(self.frame)-16, CGRectGetHeight(placeHolderLabel.frame));
}
-(void)setPlaceholder:(NSString *)placeholder
{
_placeholder = placeholder;
if ( placeHolderLabel == nil )
{
placeHolderLabel = [[UILabel alloc] init];
placeHolderLabel.autoresizingMask = (UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight);
placeHolderLabel.lineBreakMode = NSLineBreakByWordWrapping;
placeHolderLabel.numberOfLines = 0;
placeHolderLabel.font = self.font;
placeHolderLabel.backgroundColor = [UIColor clearColor];
placeHolderLabel.textColor = [UIColor colorWithWhite:0.7 alpha:1.0];
placeHolderLabel.alpha = 0;
[self addSubview:placeHolderLabel];
}
placeHolderLabel.text = self.placeholder;
[self refreshPlaceholder];
}
//When any text changes on textField, the delegate getter is called. At this time we refresh the textView's placeholder
// 代理的get方法中刷新占位文字狀態(tài)
-(id<UITextViewDelegate>)delegate
{
[self refreshPlaceholder];
return [super delegate];
}
@end
- IBInspectable 關(guān)鍵字
在類的聲明中,placeholder屬性聲明為IBInspectable.這是什么意思呢?
標(biāo)有 @IBInspectable(或是 Objective-C 中的 IBInspectable),他們就可以很容易在 Interface Builder 的觀察面板(inspector panel)里編輯。需要注意的是 Xcode 在這里做了更多的事,屬性名稱是從 camel- 轉(zhuǎn)換為 title- 模式 并且相關(guān)的名稱組合在一起:因?yàn)榭蓹z查屬性僅僅是用戶定義的運(yùn)行時(shí)屬性頂部的接口,所以支持相同的類型列表:布爾,字符串和數(shù)字(即,NSNumber 或任何數(shù)值類型),以及 CGPoint、CGSize、CGRect、UIColor 和 NSRange,額外增加了 UIImage。
如圖Interface Builder 中多了placeholder屬性:

擴(kuò)展: IBDesignable 關(guān)鍵字
當(dāng)應(yīng)用到 UIView 或 NSView 子類中的時(shí)候,@ IBDesignable 讓 Interface Builder 知道它應(yīng)該在畫布上直接渲染視圖。你會(huì)看到你的自定義視圖在每次更改后不必編譯并運(yùn)行你的應(yīng)用程序就會(huì)顯示。
標(biāo)記一個(gè)自定義視圖為 IBDesignable,只需在類名前加上 @IBDesignable 的前綴(或是 Objective-C 里的 IB_DESIGNABLE 宏)。你的初始化、布置和繪制方法將被用來在畫布上渲染你的自定義視圖:

使用方法:
swift中:
@IBDesignable
class View: UIView { .... }
OC中:
IB_DESIGNABLE
@implementation View
- @proprety 和 @synthesize 、 @dynamic 關(guān)鍵字
@proprety = ivar + getter + setter
在聲明中寫@property(nullable, nonatomic,copy) IBInspectable NSString *placeholder; 這句代碼后,編譯器會(huì)自動(dòng)寫出一套 存 、 取 方法.這個(gè)過程由編譯 器在編譯期執(zhí)行,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼.除了生成方法代碼 getter、setter 之外,編譯器還要 自動(dòng)向類中添加適當(dāng)類型的實(shí)例變量 ,并且 在屬性名前面加下劃線 ,以此作為實(shí)例變量的名字.
也可以在類的實(shí)現(xiàn)代碼里通過 @synthesize 語法來指定實(shí)例變量的名字.
@implementation IQTextView
@synthesize placeholder = _myPlaceholder;
...
@end
@property有兩個(gè) 對(duì)應(yīng) 的詞,一個(gè)是@synthesize,一個(gè)是@dynamic.如果@synthesize和@dynamic都沒寫,那么默認(rèn)的就是@syntheszie placeholder = _myPlaceholder;
@synthesize的語義是如果你沒有手動(dòng)實(shí)現(xiàn)setter方法和getter方法,那么編譯器會(huì)自動(dòng)為你加上這兩個(gè)方法。
@dynamic告訴編譯器:屬性的setter與getter方法由用戶自己實(shí)現(xiàn),不自動(dòng)生成.(當(dāng)然對(duì)于readonly的屬性只需提供getter即可).假如一個(gè)屬性被聲明為@dynamic var,然后你沒有提供@setter方法和@getter方法,編譯的時(shí)候沒問題,但是當(dāng)程序運(yùn)行到 instance.var = someVar ,由于缺setter方法會(huì)導(dǎo)致程序崩潰;或者當(dāng)運(yùn)行到 someVar = var 時(shí),由于缺getter方法同樣會(huì)導(dǎo)致崩潰。編譯時(shí)沒問題,運(yùn)行時(shí)才執(zhí)行相應(yīng)的方法,這就是所謂的動(dòng)態(tài)綁定。
- setNeedsLayout 、 layoutIfNeeded 關(guān)鍵字
參考 http://www.itdecent.cn/p/eb2c4bb4e3f1
方法二:
- (void)drawRect:(CGRect)rect {
if (self.hasText) return;
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
dic[NSFontAttributeName] = [UIFont systemFontOfSize:17];
dic[NSForegroundColorAttributeName] = [UIColor grayColor];
[self.placeholder drawInRect:CGRectMake(4, 5, self.bounds.size.width, self.bounds.size.height) withAttributes:dic];
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextViewTextDidChangeNotification object:nil];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextViewTextDidChangeNotification object:nil];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)textChange{
[self setNeedsDisplay]; //這個(gè)方法會(huì)調(diào)用drawRect方法。
}