簡易總結(jié)封裝一個帶有占位文字的UITextField/TextView

占位文字

  • 1礁阁、曾經(jīng)有個這么一個項目需求: 使用textField時,占位文字默認(rèn)是黑色的,我們的需求是當(dāng)開始編輯時,讓占位文字和光標(biāo)變成紅色(或其他顏色)
    • 思路: textField和button類似,內(nèi)部都擁有子控件,在OC機制中,所有控件內(nèi)部都是以懶加載的形式添加的.我們可以拿到textField中的子控件label,通過監(jiān)聽textField的狀態(tài),設(shè)置內(nèi)部子控件label的樣式.

  • 2、當(dāng)系統(tǒng)自帶的控件滿足不了項目要求時
    • 對于 UITextField :

    1> 有占位文字 2> 最多只能輸入一行文字

    • 對于UITextView

    1> 沒有占位文字 2> 能輸入任意行文字陆馁,并且超出顯示范圍的可以滾動(UITextView繼承自UIScrollView)

    • 而我們需要的結(jié)果 :

1> 有占位文字 2> 能輸入任意行文字
那么我們可以用腳想一想便可選擇自定義控件繼承于UITextField /UITextView,實現(xiàn)添加其占位文字的功能绢淀。


  • 3慌申、以后開發(fā)中可能經(jīng)常會使用到這種技術(shù)访圃,所以我現(xiàn)在就總結(jié)一下如何弄占位文字以及修改占位文字的顏色替蔬,方便以后開發(fā)(只提供了一種思路告私,其他方法思路自己可以總結(jié))。
    • 注意 : 以下方法我們最好是將它封裝到一個分類中,提高代碼的復(fù)用和封裝性.

以下是封裝的代碼:

一承桥、UITextField

1.第一種方法:UITextField利用RunTime來設(shè)置占位文字的顏色

主要思路: 方法二的主要思路是利用KVC思想,拿到TextFiled內(nèi)部中的子控件,在使用KVC之前,用runtime變出TextFiled中所有子控件,找到placeholderLabel即可.

########################### .h文件 ###########################
//  UITextField+LYMPlaceholderColor.m
//  Created by ming on 14/12/20.
//  Copyright ? 2014年 ming. All rights reserved.

#import <UIKit/UIKit.h>
@interface UITextField (LYMPlaceholderColor)
/** 占位顏色 */
@property (nonatomic,strong) UIColor *placeholderColor;
/** 名字 */
@property (nonatomic,copy) NSString *name;
@end

########################### .m文件 ###########################
//  UITextField+LYMPlaceholderColor.m
//  Created by ming on 14/12/20.
//  Copyright ? 2014年 ming. All rights reserved.
/**
 *  利用RunTime來設(shè)置占位文字的顏色
 */


#import "UITextField+LYMPlaceholderColor.h"
// 導(dǎo)入頭文件驻粟,導(dǎo)入下面其中一個即可
#import <objc/runtime.h>
//#import <objc/message.h>

// OC最喜歡懶加載,用的的時候才會去加載
// 需要給系統(tǒng)UITextField添加屬性,只能使用runtime

static NSString *const LYMPlaceholderLabelKey = @"placeholderLabel";
static NSString *const placeholderColorName = @"placeholderColor";
@implementation UITextField (LYMPlaceholderColor)

#pragma mark - 利用RunTime動態(tài)增加屬性和交換方法 =
/// 實現(xiàn)交換方法 (mg_setPlaceholder:和setPlaceholder:的互換)
+ (void)load{
    Method mg_setPlaceholder = class_getInstanceMethod(self, @selector(mg_setPlaceholder:));
    
    Method setPlaceholder = class_getInstanceMethod(self, @selector(setPlaceholder:));
    
    method_exchangeImplementations(setPlaceholder, mg_setPlaceholder);
}

/// 外界賦值占位顏色的時候就會調(diào)用
- (void)setPlaceholderColor:(UIColor *)placeholderColor{
    // 動態(tài)增加placeholderColor屬性
    objc_setAssociatedObject(self, (__bridge const void *)(placeholderColorName), placeholderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    // 設(shè)置顏色
   UILabel *placeholderLabel = [self valueForKeyPath:LYMPlaceholderLabelKey];
    placeholderLabel.textColor = placeholderColor;
}
- (UIColor *)placeholderColor{
    //    return  _placeholderColor;
    return objc_getAssociatedObject(self,(__bridge const void *)(placeholderColorName));
}

/// 外界賦值占位文字的時候會調(diào)用(自定義的方法,用來和系統(tǒng)的方法交換)
- (void)mg_setPlaceholder:(NSString *)placeholder{
    // 1.設(shè)置占位文字
    [self mg_setPlaceholder:placeholder];
   
    // 2.設(shè)置占位文字顏色
    self.placeholderColor = self.placeholderColor;
}


#pragma mark - 測試RunTime動態(tài)增加屬性
- (void)setName:(NSString *)name{
    // 動態(tài)增加“name”屬性
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name{
    return objc_getAssociatedObject(self, @"name");
}

@end

總結(jié):

1, 文本框和按鈕一樣,都可以編輯文字,所以內(nèi)部是有l(wèi)abel的,所以需要拿到文本框中的label(可以在"小面包中檢測"),當(dāng)輸入文字后,有個label就會消失,那個就是占位label,所以需要拿到系統(tǒng)內(nèi)部的私有屬性,但是不能直接拿到私有的屬性和方法,所以需要用到KVC去取值和賦值.通過"運行時"拿到屬性
2, 然后通過KVC取值
容易出錯點: 不要用setValu:forKey,程序會崩掉,要用forKeyPath:表示不管你在文件的那一層都能去拿到


2.第二種方法:UITextField 設(shè)置占位文字的顏色,讓外界直接使用(這種方法有點投機取巧)

--這樣設(shè)置相對上一中方法來說就相對比較簡潔

########################### .h文件 ###########################

#import <UIKit/UIKit.h>

@interface UITextField (placeholderColor)

/** 占位顏色 */
@property (nonatomic,strong) UIColor *placeholderColor;

@end

########################### .m文件 ###########################
/**
 *  設(shè)置占位文字的顏色,讓外界直接使用
 */

#import "UITextField+placeholderColor.h"

static NSString *const placeholderColorKey = @"placeholderLabel.textColor";

@implementation UITextField (placeholderColor)
// 重寫placeholderColor的setter方法
- (void)setPlaceholderColor:(UIColor *)placeholderColor{
    // bool屬性凶异,有文字就這設(shè)置為YES
    BOOL change = NO;
    // 如果當(dāng)前placeholder文字為空蜀撑,那么就隨便賦值幾個文字,讓它不為空
    if (self.placeholder == nil) {
        self.placeholder = @"mingge";
        // 設(shè)置 change = YES
        change = YES;
    }
    [self setValue:placeholderColor forKey:placeholderColorKey];
    // 如果change = YES,那么要把placeholder文字再次設(shè)為空
    if (change) {
        self.placeholder = nil;
    }
}

// 重寫placeholderColor的getter方法
- (UIColor *)placeholderColor{
    return [self valueForKeyPath:placeholderColorKey];
}

@end

二剩彬、UITextView

1.第一種方法:UITextView利用

Quartz2D繪圖 來設(shè)置占位文字以及顏色
// 重繪
[self setNeedsDisplay];

########################### .h文件 ###########################
//  LYMTextView.h
//  Created by ming on  14/12/9.
//  Copyright ? 2014年 ming. All rights reserved.

#import <UIKit/UIKit.h>

@interface LYMTextView : UITextView
/** 占位文字 */
@property (nonatomic,copy) NSString *placeholder;
/** 文字顏色 */
@property (nonatomic,strong) UIColor *placeholderColor;
@end

########################### .m文件 ###########################
/**
 *   給UITTextView顯示占位文字的功能屯掖,以后如需使用,就可以直接拿去用
 */
#import "LYMTextView.h"

@implementation LYMTextView

#pragma mark ========== 通知 =============
- (instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]){
        self.textColor = [UIColor blackColor];
        
        // 設(shè)置占位文字的默認(rèn)字體顏色和字體大小
        self.placeholderColor = [UIColor grayColor];
        self.font = [UIFont systemFontOfSize:15];
        
        // 發(fā)布通知(當(dāng)空間的內(nèi)容發(fā)生改變時)
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:UITextViewTextDidChangeNotification object:self];
    }
    return self;
}

- (void)textDidChange:(NSNotification *)note{
    // 重繪
    [self setNeedsDisplay];
}

// 移除監(jiān)聽者
- (void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

#pragma mark ========== 繪制占位文字 ===========
// 繪制占位文字
- (void)drawRect:(CGRect)rect {
    // 如果有文字就不繪制(不執(zhí)行下面的操作)
    if (self.text.length) return;
    
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
//    if (self.font) dict[NSFontAttributeName] = self.font;
//    if (self.placeholderColor)  dict[NSForegroundColorAttributeName] = self.placeholderColor;
     dict[NSFontAttributeName] = self.font;
     dict[NSForegroundColorAttributeName] = self.placeholderColor;

    
    rect.origin.x = 4;
    rect.origin.y = 8;
    rect.size.width = LYMScreenWidth - 2 * rect.origin.x;
    
    [self.placeholder drawInRect:rect withAttributes:dict];
}

#pragma mark ========== 需要重寫的屬性 ===========
// 重寫占位文字
- (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];
}

// textView的尺寸發(fā)生改變
- (void)layoutSubviews{
    [super layoutSubviews];
    // 重繪
    [self setNeedsDisplay];
}

@end

2.第二種方法:UITextView利用

刷新 來設(shè)置占位文字以及顏色
// 重新布局
[self setNeedsLayout];

########################### .h文件 ###########################
//  LYMTextViewWithLabel.h
//  Created by ming on 14/12/9.
//  Copyright ? 2014年 ming. All rights reserved.

#import <UIKit/UIKit.h>

@interface LYMTextViewWithLabel : UITextView
/** 占位文字 */
@property (nonatomic,copy) NSString *placeholder;
/** 文字顏色 */
@property (nonatomic,strong) UIColor *placeholderColor;
@end

########################### .m文件 ###########################
#import "LYMTextViewWithLabel.h"

@interface LYMTextViewWithLabel ()
/** 占位Label */
@property (nonatomic,weak)  UILabel *placeholderLabel;
@end

@implementation LYMTextViewWithLabel

#pragma mark ========== 通知 =============
- (instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]){
        // 創(chuàng)建一個UILabel
        UILabel *placeholderLabel = [[UILabel alloc] init];
        placeholderLabel.numberOfLines = 0;
        [self addSubview:placeholderLabel];
        self.placeholderLabel = placeholderLabel;
        
        // 設(shè)置占位文字的默認(rèn)字體顏色和字體大小
        self.textColor = [UIColor blackColor];
        self.font = [UIFont systemFontOfSize:15];
        
        // 發(fā)布通知(當(dāng)空間的內(nèi)容發(fā)生改變時)
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:UITextViewTextDidChangeNotification object:self];
    }
    return self;
}

// 布局占位文字的位置和尺寸
- (void)layoutSubviews{
    [super layoutSubviews];
    self.placeholderLabel.x = 4;
    self.placeholderLabel.y = 8;
    self.placeholderLabel.width = self.width - 2*self.placeholderLabel.x;
    // 自適應(yīng)
    [self.placeholderLabel sizeToFit];
}

- (void)textDidChange:(NSNotification *)note{
    // 是否隱藏
    self.placeholderLabel.hidden = self.hasText;
}

// 移除監(jiān)聽者
- (void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


- (void)setPlaceholderColor:(UIColor *)placeholderColor{
    self.placeholderLabel.textColor = placeholderColor;
}

#pragma mark ========== 重新計算placeholderLabel的尺寸 =============
- (void)setFont:(UIFont *)font{
    [super setFont:font];
    self.placeholderLabel.font = self.font;
    // 重新布局
    [self setNeedsLayout];
}

- (void)setPlaceholder:(NSString *)placeholder{
    self.placeholderLabel.text = [placeholder copy];
    // 重新布局
    [self setNeedsLayout];
}

#pragma mark ========== 隱藏placeholderLabel =============
- (void)setText:(NSString *)text{
    [super setText:text];
    // 根據(jù)是否有文字來判斷要不要隱藏
    self.placeholderLabel.hidden = self.hasText;
}

- (void)setAttributedText:(NSAttributedString *)attributedText{
    [super setAttributedText:attributedText];
    // 根據(jù)是否有文字來判斷要不要隱藏
    self.placeholderLabel.hidden = self.hasText;
}

@end

3.runTime

  • 頭文件
#import <objc/runtime.h>
#import <objc/message.h>```

- viewDidLoad

  • (void)viewDidLoad {
    [super viewDidLoad];  
    // 通過運行時襟衰,發(fā)現(xiàn)UITextView有一個叫做“_placeHolderLabel”的私有變量
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([UITextView class], &count);
    for (int i = 0; i < count; i++) {
    Ivar ivar = ivars[i];
    const char *name = ivar_getName(ivar);
    NSString *objcName = [NSString stringWithUTF8String:name];
    NSLog(@"%d : %@",i,objcName);
    }
    [self setupTextView];
    }```

  • setupTextView

- (void)setupTextView{
        // 提示文字
        CGFloat margin  = 15;
        
        UILabel *tipLabel = [[UILabel alloc] initWithFrame:CGRectMake(margin, 0, MGSCREEN_width - 2 * margin, 50)];
        tipLabel.text = @"你的批評和建議能幫助我們更好的完善產(chǎn)品,請留下你的寶貴意見!";
        tipLabel.numberOfLines = 2;
        tipLabel.textColor = MGRGBColor(255, 10, 10);
        tipLabel.font = MGFont(16);
        [self.view addSubview:tipLabel];
        // 意見輸入框
        CGFloat height  = 200;
#ifndef __IPHONE_4_0
        height = 100;
#endif
        UITextView *iderTextView = [[UITextView alloc] initWithFrame:CGRectMake(margin, CGRectGetMaxY(tipLabel.frame) + margin, MGSCREEN_width - 2 * margin, height)];
        iderTextView.backgroundColor = [UIColor whiteColor];
        iderTextView.scrollEnabled = YES;
        iderTextView.scrollsToTop = YES;
        iderTextView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
//    iderTextView.delegate = self;
        self.iderTextView = iderTextView;
        [self.view addSubview:iderTextView];
    
        // _placeholderLabel
        UILabel *placeHolderLabel = [[UILabel alloc] init];
        placeHolderLabel.text = @"請輸入寶貴意見(300字以內(nèi))";
        placeHolderLabel.numberOfLines = 0;
        placeHolderLabel.font = [UIFont systemFontOfSize:14];
        placeHolderLabel.textColor = [UIColor lightGrayColor];
        [placeHolderLabel sizeToFit];
        [iderTextView addSubview:placeHolderLabel];
    
        [iderTextView setValue:placeHolderLabel forKey:@"_placeholderLabel"];
        iderTextView.font =  placeHolderLabel.font;
}

補充:一種最簡單的實現(xiàn)占位文字的textView(該方法有點投機取巧)

  • viewDidLoad
- (void)viewDidLoad {
    [super viewDidLoad];
    //    意見輸入
    myTextView=[[UITextView alloc] initWithFrame:(CGRectMake(10, 33, kScreenWidth-20, 130))];
    myTextView.font=kFont(15);
    myTextView.delegate=self;
    myTextView.backgroundColor=self.view.backgroundColor;
    ViewBorderRadius(myTextView, 0, 0.5f, kGray);
    myTextView.textColor=kGray;
    myTextView.text=@" 請輸入詳細(xì)描述";
    myTextView.tintColor=kWhite;
    [self.view addSubview:myTextView];
    //    提交
    UIButton *feedBtn=[UIButton buttonWithType:(UIButtonTypeCustom)];
    feedBtn.frame=CGRectMake(100, myTextView.tail+40, kScreenWidth-200, 40);
    [feedBtn addTarget:self action:@selector(feedBtnHandled:) forControlEvents:(UIControlEventTouchUpInside)];
    [feedBtn setTitle:@"提交" forState:(UIControlStateNormal)];
    [feedBtn setTitleColor:[UIColor whiteColor] forState:(UIControlStateNormal)];
    feedBtn.titleLabel.font=kFont(18);
    feedBtn.backgroundColor=naBarTiniColor;
    [self.view addSubview:feedBtn];

}```

- UITextViewDelegate

  • (void)textViewDidBeginEditing:(UITextView *)textView{
    if([textView.text isEqualToString:@" 請輸入詳細(xì)描述"]){
    textView.text=@"";
    textView.textColor=kWhite;
    }
    }

  • (void)textViewDidEndEditing:(UITextView *)textView{

    if([textView.text isEmptyString]){

      textView.text=@" 請輸入詳細(xì)描述";
      textView.textColor=kGray;
    

    }
    }

![未輸入時](http://upload-images.jianshu.io/upload_images/1429890-73a3f245a9ced10a.PNG)
![UITextView成為第一響應(yīng)者時](http://upload-images.jianshu.io/upload_images/1429890-10b06997fb7af974.PNG)
***
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贴铜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子瀑晒,更是在濱河造成了極大的恐慌绍坝,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苔悦,死亡現(xiàn)場離奇詭異轩褐,居然都是意外死亡,警方通過查閱死者的電腦和手機玖详,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門把介,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蟋座,你說我怎么就攤上這事拗踢。” “怎么了向臀?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵巢墅,是天一觀的道長。 經(jīng)常有香客問我券膀,道長君纫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任芹彬,我火速辦了婚禮蓄髓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舒帮。我一直安慰自己会喝,他們只是感情好眨唬,可當(dāng)我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著好乐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瓦宜。 梳的紋絲不亂的頭發(fā)上蔚万,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音临庇,去河邊找鬼反璃。 笑死,一個胖子當(dāng)著我的面吹牛假夺,可吹牛的內(nèi)容都是我干的淮蜈。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼已卷,長吁一口氣:“原來是場噩夢啊……” “哼梧田!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起侧蘸,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤裁眯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后讳癌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體穿稳,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年委乌,在試婚紗的時候發(fā)現(xiàn)自己被綠了侧戴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鳞青。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖它改,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情商乎,我是刑警寧澤搔课,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站截亦,受9級特大地震影響爬泥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜崩瓤,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一袍啡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧却桶,春花似錦境输、人聲如沸蔗牡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辩越。三九已至,卻和暖如春信粮,著一層夾襖步出監(jiān)牢的瞬間黔攒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工强缘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留督惰,地道東北人。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓旅掂,卻偏偏與公主長得像赏胚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子商虐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容