需求:
1.通過(guò)不同狀態(tài)調(diào)整TextField占位文字顏色
2.編輯狀態(tài)下為WhiteColor;默認(rèn)LightGrayColor
文字描述較為抽象,直接看效果圖:

實(shí)現(xiàn)方式一
實(shí)現(xiàn)思路:
設(shè)置TextField代理,實(shí)現(xiàn)<UITextFieldDelegate>協(xié)議方法,在開(kāi)始編輯時(shí),通過(guò)富文本直接對(duì)TextField的attributedPlaceholder賦值,同理,在編輯結(jié)束后同樣操作
當(dāng)然,在為TextField設(shè)置占位文字時(shí),也需要通過(guò)富文本對(duì)attributedPlaceholder屬性進(jìn)行賦值
- (void)textFieldDidBeginEditing:(JSLoginTextField *)textField {
NSMutableAttributedString *placeholderText = [[NSMutableAttributedString alloc] initWithAttributedString:textField.attributedPlaceholder];
[placeholderText addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0, placeholderText.length)];
textField.attributedPlaceholder = placeholderText;
}
- (void)textFieldDidEndEditing:(JSLoginTextField *)textField {
NSMutableAttributedString *placeholderText = [[NSMutableAttributedString alloc] initWithAttributedString:textField.attributedPlaceholder];
[placeholderText addAttribute:NSForegroundColorAttributeName value:[UIColor lightGrayColor] range:NSMakeRange(0, placeholderText.length)];
textField.attributedPlaceholder = placeholderText;
}
實(shí)現(xiàn)方式二
實(shí)現(xiàn)思路:
實(shí)現(xiàn)代理的方式有點(diǎn)大材小用了,其實(shí)還可以addTarget的方式
分別在UIControlEventEditingDidBegin和UIControlEventEditingDidEnd中做上面的操作
實(shí)現(xiàn)方式三
實(shí)現(xiàn)思路:
通知,在自定義TextField中,分別監(jiān)聽(tīng)UITextFieldTextDidBeginEditingNotification和UITextFieldTextDidEndEditingNotification,在接收到對(duì)應(yīng)通知后,在方法中執(zhí)行上面的操作
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(beingEditing:) name:UITextFieldTextDidBeginEditingNotification object:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(endEditing:) name:UITextFieldTextDidEndEditingNotification object:self];
注意點(diǎn):
自定義的
TextField默認(rèn)注冊(cè)了通知,也就意味著實(shí)例化出的每一個(gè)實(shí)例都注冊(cè)了通知,因此一旦接受到了通知,整個(gè)視圖內(nèi)自定義的TextField都會(huì)去響應(yīng)對(duì)應(yīng)的方法
e.g. 我這個(gè)界面,有兩個(gè)文本框(賬號(hào)和密碼),當(dāng)賬號(hào)輸入框獲取焦點(diǎn)時(shí),密碼也接收到了通知,修改占位文本框字體顏色會(huì)讓兩個(gè)文本框同時(shí)發(fā)生改變,并不能滿足我們的需求(獲取焦點(diǎn)時(shí)白色,失去焦點(diǎn)的灰色),所以要做限制處理,讓賬號(hào)和密碼輸入框只監(jiān)聽(tīng)自己發(fā)出的通知,也就是在自定義TextField注冊(cè)通知時(shí),將object參數(shù)傳入自己,這樣就滿足了每一個(gè)文本框只監(jiān)聽(tīng)自己發(fā)出的通知在收到
TextField發(fā)布通知執(zhí)行的方法中,通過(guò)參數(shù)notification.object獲取到對(duì)應(yīng)的TextField,執(zhí)行后面的操作使用通知,切記在
dealloc中移除通知
實(shí)現(xiàn)方式四
實(shí)現(xiàn)思路:
1.自定義TextField;
2.通過(guò)RunTime獲取到私有屬性placeholderLabel占位文本框;
3.重寫(xiě)layoutSubviews方法,根據(jù)當(dāng)前TextField狀態(tài)通過(guò)KVC設(shè)置占位文本框字體顏色
- (void)layoutSubviews {
[super layoutSubviews];
if (self.isEditing) {
[self setValue:[UIColor whiteColor] forKeyPath:@"placeholderLabel.textColor"];
} else {
// KVC 修改 占位文字顏色
[self setValue:[UIColor lightGrayColor] forKeyPath:@"placeholderLabel.textColor"];
}
}
實(shí)現(xiàn)方式五
實(shí)現(xiàn)思路:
重寫(xiě)becomeFirstResponder和resignFirstResponder方法,分別對(duì)當(dāng)前TextField處理
- (void)layoutSubviews {
[super layoutSubviews];
if (!self.isEditing) {
[self setValue:[UIColor lightGrayColor] forKeyPath:@"placeholderLabel.textColor"];
}
}
- (BOOL)becomeFirstResponder {
[self setValue:[UIColor whiteColor] forKeyPath:@"placeholderLabel.textColor"];
return [super becomeFirstResponder];
}
- (BOOL)resignFirstResponder {
[self setValue:[UIColor lightGrayColor] forKeyPath:@"placeholderLabel.textColor"];
return [super resignFirstResponder];
}
但需要注意一點(diǎn),因?yàn)?code>TextField在實(shí)例化時(shí),默認(rèn)并未設(shè)置placeholder屬性,TextField內(nèi)部子控件placeholderLabel采用一種懶加載機(jī)制,此時(shí)相當(dāng)于為nil,如果在自定義TextField的init方法中直接通過(guò)KVC方式設(shè)置默認(rèn)狀態(tài)顏色是無(wú)意的,必須要保證在placeholderLabel存在的前提下設(shè)置才會(huì)有意義,而設(shè)置延遲的方式并不能完美解決這個(gè)問(wèn)題,文字描述可能比較抽空,直接看圖便能更容易的發(fā)現(xiàn)這個(gè)問(wèn)題:

雖然beignEdit和endEdit下的狀態(tài)正常,但是剛剛展示視圖時(shí),TextField的占位文字還是黑色,暫時(shí)想到的方法就是在layoutSubViews方法中(方式四中),將未編輯狀態(tài)下設(shè)置成灰色
總結(jié):
- 方式一和方式二比較簡(jiǎn)單粗暴,思路上仍以理解,直接利用了
TextField的attributedPlaceholder屬性,使用了富文本,而且需要在父視圖設(shè)置代理,實(shí)現(xiàn)協(xié)議方法或?qū)崿F(xiàn)監(jiān)聽(tīng)事件;
當(dāng)然通過(guò)
協(xié)議/addTarget的方式,也可以封裝到TextField內(nèi)部
① 將代理設(shè)置給自己,實(shí)現(xiàn)協(xié)議方法,但這種將代理設(shè)置給自己的方式不推薦使用,因?yàn)榇砟J綖橐粚?duì)一,我們將TextField的代理對(duì)象設(shè)置給了自己,而外界使用TextField并設(shè)置代理后,問(wèn)題就來(lái)了
② 而addTarget的方式不受影響,在TextField內(nèi)部添加自己監(jiān)聽(tīng)狀態(tài),在外界添加控制器監(jiān)聽(tīng)TextField狀態(tài),當(dāng)條件滿足時(shí),TextField和控制器都會(huì)分別響應(yīng)自身內(nèi)部的事件,不受影響
- 方式三,通知和代理、addTarget的思路類似,但是性能略差一些,而且照此需求實(shí)現(xiàn)的注意點(diǎn)相對(duì)其他方式較多
通知的特點(diǎn)是方法執(zhí)行所在線程取決于監(jiān)聽(tīng)通知的位置,比如在主線程監(jiān)聽(tīng)通知,那么收到通知后,方法將在主線程執(zhí)行,反之則在子線程,而大部分的系統(tǒng)方法均在主線程執(zhí)行,除了最常使用的
addObserver:,還可以用另外一種指定線程的方式:
self.observer = [[NSNotificationCenter defaultCenter] addObserverForName:UITextFieldTextDidBeginEditingNotification object:self queue:[[NSOperationQueue alloc] init] usingBlock:^(NSNotification * _Nonnull note) {
// 執(zhí)行代碼
}];
- 方式四會(huì)使父視圖或控制器的代碼更加簡(jiǎn)潔,關(guān)于TextField樣式的問(wèn)題,完全封裝到自定義控件內(nèi)部,不需要外界考慮,使用更加靈活,同時(shí)又不需要過(guò)多的考慮,比如通知的監(jiān)聽(tīng)、移除,代理對(duì)外的影響等.
- 方式五的實(shí)現(xiàn)也較為簡(jiǎn)單,只是需要注意
TextField初始默認(rèn)狀態(tài)的設(shè)置時(shí)機(jī)