前言:在開(kāi)發(fā)過(guò)程當(dāng)中奕锌,我們很多時(shí)候會(huì)遇到在textView中像textFiled用到占位符的情況,也就是一個(gè)placeHold村生。但是系統(tǒng)并沒(méi)有給textView提供這個(gè)屬性惊暴,所以我們需要自己來(lái)實(shí)現(xiàn)。正好寫(xiě)項(xiàng)目遇到這種情況趁桃,就把我知道的幾種能實(shí)現(xiàn)的方式都實(shí)現(xiàn)了出來(lái)辽话。
-
第一種方式 簡(jiǎn)單粗暴
這種方法的特點(diǎn)是,當(dāng)用戶點(diǎn)擊了TextView的卫病,占位符占位文字就會(huì)立馬消失油啤,官方的占位符是當(dāng)系統(tǒng)監(jiān)聽(tīng)到用戶輸入了文字后占位符才會(huì)消失.
// 1.把UITextView的文本屬性當(dāng)成“placeholder”使用
// 2.在開(kāi)始編輯的代理方法里清除“placeholder”
// 3.在結(jié)束編輯的代理方法里根據(jù)條件設(shè)置“placeholder”。
// 創(chuàng)建textView
UITextView *textView =[[UITextView alloc]initWithFrame:CGRectMake(20,70,[UIScreen mainScreen].bounds.size.width-40,200)];
textView.backgroundColor= [UIColor grayColor];
textView.text = @"這種方法的特點(diǎn)是蟀苛,當(dāng)用戶點(diǎn)擊了TextView的益咬,占位符占位文字就會(huì)立馬消失,官方的占位符是當(dāng)系統(tǒng)監(jiān)聽(tīng)到用戶輸入了文字后占位符才會(huì)消失";
textView.textColor = [UIColor lightGrayColor];
textView.delegate = self;
[self.view addSubview:textView];
代理方法:
#pragma mark - UITextViewDelegate
- (void)textViewDidEndEditing:(UITextView *)textView
{
if(textView.text.length < 1){
textView.text = @"這種方法的特點(diǎn)是帜平,當(dāng)用戶點(diǎn)擊了TextView的幽告,占位符占位文字就會(huì)立馬消失,官方的占位符是當(dāng)系統(tǒng)監(jiān)聽(tīng)到用戶輸入了文字后占位符才會(huì)消失";
textView.textColor = [UIColor grayColor];
}
}
- (void)textViewDidBeginEditing:(UITextView *)textView
{
if([textView.text isEqualToString:@"這種方法的特點(diǎn)是裆甩,當(dāng)用戶點(diǎn)擊了TextView的冗锁,占位符占位文字就會(huì)立馬消失,官方的占位符是當(dāng)系統(tǒng)監(jiān)聽(tīng)到用戶輸入了文字后占位符才會(huì)消失"]){
textView.text=@"";
textView.textColor=[UIColor blackColor];
}
}
-
第二種
該方法同樣也可以實(shí)現(xiàn)類(lèi)似于占位符的功能相比較方法一淑掌,方法二可以實(shí)現(xiàn)動(dòng)態(tài)監(jiān)聽(tīng)文本的改變蒿讥,并非彈出鍵盤(pán)就立即清除占位符,只有當(dāng)用戶開(kāi)始輸入文本的時(shí)候.placeholder才會(huì)消失抛腕。同樣芋绸,當(dāng)用戶清空文本的時(shí)候,占位符又會(huì)重新顯示出來(lái)担敌。
這種方法最致命的弊端在于要設(shè)置textView的偏移來(lái)設(shè)置label的位置摔敛,不好控制
// 1.創(chuàng)建textView
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(10, 74, [UIScreen mainScreen].bounds.size.width - 20, 200)];
textView.backgroundColor = [UIColor grayColor];
[self.view addSubview:textView];
self.textView = textView;
self.textView.delegate = self;
textView.contentInset = UIEdgeInsetsMake(-30, 0, 0, 0);
// 2.給textView添加一個(gè)UILabel子控件,作為占位符
UILabel *placeHolder = [[UILabel alloc] initWithFrame:CGRectMake(15, 0, [UIScreen mainScreen].bounds.size.width - 50, 200)];
self.placeHolder = placeHolder;
placeHolder.text = @"該方法同樣也可以實(shí)現(xiàn)類(lèi)似于占位符的功能相比較方法一全封,方法二可以實(shí)現(xiàn)動(dòng)態(tài)監(jiān)聽(tīng)文本的改變马昙,并非彈出鍵盤(pán)就立即清除占位符,只有當(dāng)用戶開(kāi)始輸入文本的時(shí)候.placeholder才會(huì)消失刹悴。同樣行楞,當(dāng)用戶清空文本的時(shí)候,占位符又會(huì)重新顯示出來(lái)土匀。";
placeHolder.textColor = [UIColor lightGrayColor];
placeHolder.numberOfLines = 0;
placeHolder.contentMode = UIViewContentModeTop;
[self.textView addSubview:placeHolder];
代理方法
#pragma mark - UITextViewDelegate
- (void)textViewDidChange:(UITextView *)textView
{
if (!textView.text.length) {
self.placeHolder.alpha = 1;
} else {
self.placeHolder.alpha = 0;
}
}
-
第三種
相比計(jì)較上面兩種方法子房,這種方法可移植性、拓展性更好,這種方法证杭,不僅樂(lè)意隨意通過(guò)我們添加的placeholder屬性設(shè)置默認(rèn)文字田度,還可以通過(guò)我們添加的placeholderColor設(shè)置默認(rèn)文字的顏色。今后解愤,我們只需要寫(xiě)好這么一個(gè)自定義UITextView镇饺,·就可以一勞永逸。
// 1.自定義UITextView(繼承UITextView)
// 2.給UITextView添加placeholder和placeholderColor屬性
// 3.重寫(xiě)initWithFrame方法
// 4.添加通知監(jiān)聽(tīng)文字改變
// 5.重寫(xiě)drawRect:方法
// 6.重寫(xiě)相關(guān)屬性的set方法
@interface SYJThirdTextView : UITextView
/** 占位文字 */
@property (nonatomic, copy) NSString *placeholder;
/** 占位文字顏色 */
@property (nonatomic, strong) UIColor *placeholderColor;
@end
#.m中實(shí)現(xiàn)
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
// 設(shè)置默認(rèn)字體
self.font = [UIFont systemFontOfSize:15];
// 設(shè)置默認(rèn)顏色
self.placeholderColor = [UIColor grayColor];
// 使用通知監(jiān)聽(tīng)文字改變
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:UITextViewTextDidChangeNotification object:self];
}
return self;
}
- (void)textDidChange:(NSNotification *)note
{
// 會(huì)重新調(diào)用drawRect:方法
[self setNeedsDisplay];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
/**
* 每次調(diào)用drawRect:方法送讲,都會(huì)將以前畫(huà)的東西清除掉
*/
- (void)drawRect:(CGRect)rect
{
// 如果有文字奸笤,就直接返回,不需要畫(huà)占位文字
if (self.hasText) return;
// 屬性
NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
attrs[NSFontAttributeName] = self.font;
attrs[NSForegroundColorAttributeName] = self.placeholderColor;
// 畫(huà)文字
rect.origin.x = 5;
rect.origin.y = 8;
rect.size.width -= 2 * rect.origin.x;
[self.placeholder drawInRect:rect withAttributes:attrs];
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self setNeedsDisplay];
}
#pragma mark - setter
- (void)setPlaceholder:(NSString *)placeholder
{
_placeholder = [placeholder copy];
[self setNeedsDisplay];
}
- (void)setPlaceholderColor:(UIColor *)placeholderColor
{
_placeholderColor = placeholderColor;
[self setNeedsDisplay];
}
- (void)setFont:(UIFont *)font
{
[super setFont:font];
[self setNeedsDisplay];
}
- (void)setText:(NSString *)text
{
[super setText:text];
[self setNeedsDisplay];
}
- (void)setAttributedText:(NSAttributedString *)attributedText
{
[super setAttributedText:attributedText];
[self setNeedsDisplay];
}
SYJThirdTextView *textView = [[SYJThirdTextView alloc]init];
[self.view addSubview:textView];
textView.frame = CGRectMake(10, 100, [UIScreen mainScreen].bounds.size.width - 20, 240);
textView.placeholder = @"相比計(jì)較上面兩種方法李茫,這種方法可移植性揭保、拓展性更好,這種方法魄宏,不僅樂(lè)意隨意通過(guò)我們添加的placeholder屬性設(shè)置默認(rèn)文字秸侣,還可以通過(guò)我們添加的placeholderColor設(shè)置默認(rèn)文字的顏色。今后宠互,我們只需要寫(xiě)好這么一個(gè)自定義UITextView味榛,就可以一勞永逸。";
textView.backgroundColor = [UIColor grayColor];
//占位符顏色
textView.placeholderColor = [UIColor lightGrayColor];
-
第四種
這個(gè)方法的和方法三很相似予跌,只是沒(méi)有利用通知來(lái)監(jiān)聽(tīng)文本的改變搏色,需要配合textViewDidChanged:這個(gè)文本改變的代理方法使用。
@interface SYJFourTextView : UITextView
//1.自定義UITextView
//2.給UITextView添加placeholder和placeholderColor屬性
//3.重寫(xiě)initWithFrame方法
//4.重寫(xiě)drawRect:方法
//5.重寫(xiě)相關(guān)屬性的set方法
/** 占位文字 */
@property (nonatomic,copy) NSString *placeholder;
/** 占位文字顏色 */
@property (nonatomic,strong) UIColor *placeholderColor;
@end
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.font = [UIFont systemFontOfSize:15];
self.placeholderColor = [UIColor lightGrayColor];
self.placeholder = @"請(qǐng)輸入內(nèi)容";
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
attrs[NSFontAttributeName] = self.font;
attrs[NSForegroundColorAttributeName] = self.placeholderColor;
[self.placeholder drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height) withAttributes:attrs];
}
// 布局子控件的時(shí)候需要重繪
- (void)layoutSubviews
{
[super layoutSubviews];
[self setNeedsDisplay];
}
// 設(shè)置屬性的時(shí)候需要重繪券册,所以需要重寫(xiě)相關(guān)屬性的set方法
- (void)setPlaceholder:(NSString *)placeholder
{
_placeholder = placeholder;
[self setNeedsDisplay];
}
- (void)setPlaceholderColor:(UIColor *)placeholderColor
{
_placeholderColor = placeholderColor;
[self setNeedsDisplay];
}
- (void)setFont:(UIFont *)font
{
[super setFont:font];
[self setNeedsDisplay];
}
- (void)setText:(NSString *)text
{
[super setText:text];
if (text.length) {
// 因?yàn)槭窃谖谋靖淖兊拇矸椒ㄖ信袛嗍欠耧@示placeholder频轿,而通過(guò)代碼設(shè)置text的方式又不會(huì)調(diào)用文本改變的代理方法,所以再此根據(jù)text是否不為空判斷是否顯示placeholder烁焙。
self.placeholder = @"";
}
[self setNeedsDisplay];
}
- (void)setAttributedText:(NSAttributedString *)attributedText
{
[super setAttributedText:attributedText];
if (attributedText.length) {
self.placeholder = @"";
}
[self setNeedsDisplay];
}
SYJFourTextView *textView = [[SYJFourTextView alloc] initWithFrame:CGRectMake(10, 80, self.view.frame.size.width - 20, 240)];
textView.backgroundColor = [UIColor grayColor];
textView.placeholder = @"這個(gè)方法的和方法三很相似航邢,只是沒(méi)有利用通知來(lái)監(jiān)聽(tīng)文本的改變,需要配合textViewDidChanged:這個(gè)文本改變的代理方法使用骄蝇。";
textView.placeholderColor = [UIColor lightGrayColor];
textView.delegate = self;
[self.view addSubview:textView];
#pragma mark - UITextViewDelegate
- (void)textViewDidChange:(SYJFourTextView *)textView // 此處取巧膳殷,把代理方法參數(shù)類(lèi)型直接改成自定義的SYJFourTextView類(lèi)型,為了可以使用自定義的placeholder屬性九火,省去了通過(guò)給控制器SYJFourTextView類(lèi)型屬性這樣一步赚窃。
{
if (textView.hasText) { // textView.text.length
textView.placeholder = @"";
} else {
textView.placeholder = @"這個(gè)方法的和方法三很相似,只是沒(méi)有利用通知來(lái)監(jiān)聽(tīng)文本的改變岔激,需要配合textViewDidChanged:這個(gè)文本改變的代理方法使用";
}
}
-
第五種 <推薦>
相對(duì)于上面的4種方法勒极,這種方法更加取巧,雖然Apple官方?jīng)]有給我們開(kāi)發(fā)者提供類(lèi)似于placeholder的屬性虑鼎,但是通過(guò)運(yùn)行時(shí)辱匿,我們遍歷出了一個(gè)placeHolderLabel的私有變量。這種方法簡(jiǎn)單易懂,代碼量少掀鹅,推薦大家使用這種方法。
// 通過(guò)runtime媒楼,我們發(fā)現(xiàn)乐尊,UITextView內(nèi)部有一個(gè)名為“_placeHolderLabel”的私有成員變量。大家知道划址,Objective-C沒(méi)有絕對(duì)的私有變量扔嵌,因?yàn)槲覀兛梢酝ㄟ^(guò)KVC來(lái)訪問(wèn)私有變量。
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(10, 100, [UIScreen mainScreen].bounds.size.width -20, 240)];
[textView setBackgroundColor:[UIColor greenColor]];
[self.view addSubview:textView];
// _placeholderLabel
UILabel *placeHolderLabel = [[UILabel alloc] init];
placeHolderLabel.text = @"相對(duì)于上面的4種方法夺颤,這種方法更加取巧痢缎,雖然Apple官方?jīng)]有給我們開(kāi)發(fā)者提供類(lèi)似于placeholder的屬性,但是通過(guò)運(yùn)行時(shí)世澜,我們遍歷出了一個(gè)placeHolderLabel的私有變量独旷。這種方法簡(jiǎn)單易懂,代碼量少寥裂,推薦大家使用這種方法嵌洼。";
placeHolderLabel.numberOfLines = 0;
placeHolderLabel.textColor = [UIColor lightGrayColor];
[placeHolderLabel sizeToFit];
[textView addSubview:placeHolderLabel];
// same font
textView.font = [UIFont systemFontOfSize:13.f];
placeHolderLabel.font = [UIFont systemFontOfSize:13.f];
[textView setValue:placeHolderLabel forKey:@"_placeholderLabel"];
-
第六種(高大上)
這種方法就是比較高大上的一種了,也是第五種深入思考封恰。既然我們可以通過(guò)runtime找到一個(gè)屬性麻养,那么我們也可以通過(guò)runtime給他動(dòng)態(tài)添加屬性∨堤颍可以為T(mén)extView分類(lèi)動(dòng)態(tài)添加屬性鳖昌,無(wú)需繼承,用法方便低飒。
@interface UITextView (SYJText)
/**
* UITextView+placeholder
*/
@property (nonatomic, copy) NSString *syj_placeHolder;
/**
* IQKeyboardManager等第三方框架會(huì)讀取placeholder屬性并創(chuàng)建UIToolbar展示
*/
@property (nonatomic, copy) NSString *placeholder;
/**
* placeHolder顏色
*/
@property (nonatomic, strong) UIColor *syj_placeHolderColor;
@end
#.m
static const void *syj_placeHolderKey;
@interface UITextView ()
@property (nonatomic, readonly) UILabel *syj_placeHolderLabel;
@end
@implementation UITextView (SYJText)
+(void)load{
[super load];
//方法交換
method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"layoutSubviews")),
class_getInstanceMethod(self.class, @selector(SYJPlaceHolder_swizzling_layoutSubviews)));
method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"dealloc")),
class_getInstanceMethod(self.class, @selector(SYJPlaceHolder_swizzled_dealloc)));
method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"setText:")),
class_getInstanceMethod(self.class, @selector(SYJPlaceHolder_swizzled_setText:)));
}
#pragma mark - swizzling
- (void)SYJPlaceHolder_swizzled_dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self SYJPlaceHolder_swizzled_dealloc];
}
- (void)SYJPlaceHolder_swizzling_layoutSubviews{
if (self.syj_placeHolder) {
UIEdgeInsets textContainerInset = self.textContainerInset;
CGFloat lineFragmentPadding = self.textContainer.lineFragmentPadding;
CGFloat x = lineFragmentPadding + textContainerInset.left + self.layer.borderWidth;
CGFloat y = textContainerInset.top + self.layer.borderWidth;
CGFloat width = CGRectGetWidth(self.bounds) - x - textContainerInset.right - 2*self.layer.borderWidth;
CGFloat height = [self.syj_placeHolderLabel sizeThatFits:CGSizeMake(width, 0)].height;
self.syj_placeHolderLabel.frame = CGRectMake(x, y, width, height);
}
[self SYJPlaceHolder_swizzling_layoutSubviews];
}
- (void)SYJPlaceHolder_swizzled_setText:(NSString *)text{
[self SYJPlaceHolder_swizzled_setText:text];
if (self.syj_placeHolder) {
[self updatePlaceHolder];
}
}
#pragma mark - associated
//動(dòng)態(tài)添加屬性
//綁定syj_placeHolder屬性,作為textview的占位符
-(NSString *)syj_placeHolder{
return objc_getAssociatedObject(self, &syj_placeHolderKey);
}
-(void)setSyj_placeHolder:(NSString *)syj_placeHolder{
objc_setAssociatedObject(self, &syj_placeHolderKey, syj_placeHolder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self updatePlaceHolder];
}
-(UIColor *)syj_placeHolderColor{
return self.syj_placeHolderLabel.textColor;
}
-(void)setSyj_placeHolderColor:(UIColor *)syj_placeHolderColor{
self.syj_placeHolderLabel.textColor = syj_placeHolderColor;
}
-(NSString *)placeholder{
return self.syj_placeHolder;
}
-(void)setPlaceholder:(NSString *)placeholder{
self.syj_placeHolder = placeholder;
}
#pragma mark - update
- (void)updatePlaceHolder{
if (self.text.length) {
[self.syj_placeHolderLabel removeFromSuperview];
return;
}
self.syj_placeHolderLabel.font = self.font?self.font:self.cacutDefaultFont;
self.syj_placeHolderLabel.textAlignment = self.textAlignment;
self.syj_placeHolderLabel.text = self.syj_placeHolder;
[self insertSubview:self.syj_placeHolderLabel atIndex:0];
}
#pragma mark - lazzing
-(UILabel *)syj_placeHolderLabel{
UILabel *placeHolderLab = objc_getAssociatedObject(self, @selector(syj_placeHolderLabel));
if (!placeHolderLab) {
placeHolderLab = [[UILabel alloc] init];
placeHolderLab.numberOfLines = 0;
placeHolderLab.textColor = [UIColor lightGrayColor];
objc_setAssociatedObject(self, @selector(syj_placeHolderLabel), placeHolderLab, OBJC_ASSOCIATION_RETAIN);
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatePlaceHolder) name:UITextViewTextDidChangeNotification object:self];
}
return placeHolderLab;
}
- (UIFont *)cacutDefaultFont{
static UIFont *font = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
UITextView *textview = [[UITextView alloc] init];
textview.text = @" ";
font = textview.font;
});
return font;
}
@end
//利用runtime黑魔法及動(dòng)態(tài)添加屬性對(duì)textview添加placeHolder屬性
UITextView *text = [[UITextView alloc]initWithFrame:CGRectMake(10, 100, [UIScreen mainScreen].bounds.size.width - 20, 240)];
[self.view addSubview:text];
text.backgroundColor = [UIColor grayColor];
//設(shè)置大小要在font設(shè)置place前邊
text.font = [UIFont boldSystemFontOfSize:16];
text.syj_placeHolder = @"這個(gè)方法更加的友好,只需引入頭文件,所有文件就能直接使用占位符,不用集成其他類(lèi),建議使用這種方法";
text.syj_placeHolderColor = [UIColor lightGrayColor];
看下各種的效果
代碼上傳到github许昨,有需要可以看一下。
https://github.com/SYJshang/TextViewPlaceHolder