UITextView默認(rèn)是不帶有占位文字的,如果實現(xiàn)這個功能,大致有兩個方法:
- textView中添加一個label,讓label實現(xiàn)占位文字功能.
- 重寫drawRect方法,直接將文字畫上去.
IQKeyboradManager中實現(xiàn)這個功能用的是第一種方法,實現(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
// 寫到這里比寫成屬性懶加載好,因為只有設(shè)置占位文字的時候才需要創(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加載時也要初始化
-(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)的名稱組合在一起:因為可檢查屬性僅僅是用戶定義的運行時屬性頂部的接口沸停,所以支持相同的類型列表:布爾圃泡,字符串和數(shù)字(即浸颓,NSNumber 或任何數(shù)值類型),以及 CGPoint壮啊、CGSize嫉鲸、CGRect、UIColor 和 NSRange歹啼,額外增加了 UIImage玄渗。
如圖Interface Builder 中多了placeholder屬性:
擴展: IBDesignable 關(guān)鍵字
當(dāng)應(yīng)用到 UIView 或 NSView 子類中的時候,@ IBDesignable 讓 Interface Builder 知道它應(yīng)該在畫布上直接渲染視圖狸眼。你會看到你的自定義視圖在每次更改后不必編譯并運行你的應(yīng)用程序就會顯示藤树。
標(biāo)記一個自定義視圖為 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;
這句代碼后,編譯器會自動寫出一套 存 微王、 取 方法.這個過程由編譯 器在編譯期執(zhí)行,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼.除了生成方法代碼 getter屡限、setter 之外,編譯器還要 自動向類中添加適當(dāng)類型的實例變量 ,并且 在屬性名前面加下劃線 炕倘,以此作為實例變量的名字.
也可以在類的實現(xiàn)代碼里通過 @synthesize 語法來指定實例變量的名字.
@implementation IQTextView
@synthesize placeholder = _myPlaceholder;
...
@end
@property有兩個 對應(yīng) 的詞,一個是@synthesize,一個是@dynamic.如果@synthesize和@dynamic都沒寫钧大,那么默認(rèn)的就是@syntheszie placeholder = _myPlaceholder;
@synthesize的語義是如果你沒有手動實現(xiàn)setter方法和getter方法,那么編譯器會自動為你加上這兩個方法罩旋。
@dynamic告訴編譯器:屬性的setter與getter方法由用戶自己實現(xiàn),不自動生成.(當(dāng)然對于readonly的屬性只需提供getter即可).假如一個屬性被聲明為@dynamic var,然后你沒有提供@setter方法和@getter方法,編譯的時候沒問題,但是當(dāng)程序運行到 instance.var = someVar
,由于缺setter方法會導(dǎo)致程序崩潰啊央;或者當(dāng)運行到 someVar = var
時,由于缺getter方法同樣會導(dǎo)致崩潰涨醋。編譯時沒問題瓜饥,運行時才執(zhí)行相應(yīng)的方法,這就是所謂的動態(tài)綁定东帅。
- setNeedsLayout 压固、 layoutIfNeeded 關(guān)鍵字
參考 http://www.reibang.com/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]; //這個方法會調(diào)用drawRect方法。
}