背景
UITextField用的也夠多了,這兩天改一個“修改密碼”的bug,結果發現一旦設置了secureTextEntry之后,會有很多的坑,
這里簡單總結下:
第一天更新:
// 說明:以下所有的問題點都是New Password這個UITextField
1. keyboard type會改變
背景:
設置當前New Password的keyboardtype是UIKeyboardTypeASCIICapable,
現象:
設置textfield為暗文的時候,正常;但是設置為明文的時候,鍵盤會變化,看如下截圖(注意觀察左下角):
這個原因也沒查清楚,應該secureTextEntry切換的時候的bug.
那怎么解決呢?
簡單來說,既然是明文狀態下會出錯,那么就在點擊其他textfield的時候,將該password textfield設置為暗文即可.
即:可以監聽textfield的textfield end edit 狀態,并檢查不是FirstResponder的時候,設置secureTextEntry為YES
解決代碼如下:
- (void)textFieldChanged {
if (![self.textField isFirstResponder]) {
self.textField.secureTextEntry = YES;
}
}
2. 光標不穩定
現象:
當secureTextEntry在YES和NO之間切換的時候,理論上光標應該在最后一個字符后面,但是實際會出現當有暗文變為明文的時候,光標還停留在原來的位置的情況.如下截圖,藍色光標和內容之間的空格:
那怎么解決呢?
這個的解決方法,想的比較容易,讓光標重新刷新一下就好.
光標什么時候回刷新呢?
當輸入的text改變的時候會刷新,于是考慮當設置secureTextEntry后, 將textfield重新賦值一次,即額外手動觸發器一次text改變:
解決代碼如下:
- (void)showPasswordAction {
// fix cursor bug: reset text to refresh cursor
NSString *tempStr = self.textField.text;
self.textField.text = nil;
self.textField.text = tempStr;
[self setTextFiledSecureTextEntry:!self.textField.secureTextEntry];
}
3. text內容顯示不全
現象:
光標移到其他textfield上,然后點擊show的button,這時候就會出現部分字母顯示不全的情況:
可當光標移到這個textfiled上的時候,就會恢復正常
那怎么解決呢?
如果要解決的話,比較簡單的方式就是當點擊show (就是這個"眼睛")這個button的時候,將光標移到當前textfield上.
解決代碼如下:
- (void)showPasswordAction {
// 這種代碼一定要加注釋,否則后面加入的同學要瘋掉了...??
if (![self.textField isFirstResponder]) {
[self.textField becomeFirstResponder];
}
}
解決后:
4. 字體問題
現象:
password 和new password 設置的是相同的字體,但是會出現2者字體不一樣的狀態:
字體不對? 于是打了log看,發現本來設置的是系統字體,但是當光標移到password的時候,字體會變成new roman字體.
于是嘗試再切換secureTextEntry的時候,重新再設置下字體
//setLcFont 這個是我們自己定義的一個方法,無他爾...
[self.textField setLcFont:[LcFont br17]];
But,
But!!!
Failed!
打了log看,設置后字體是需要的br17字體,但是實際顯示的卻是錯誤的字體
后來查了半天,才發現是一個iOS的bug:
可以參考http://stackoverflow.com/questions/35293379
那怎么解決呢?
/* fix font bug:
1.font will change when secureTextEntry changed
2.iOS bug:font setting will not take effect until you set the font to nil
first
*/
[self.textField setFont:nil];
[self.textField setLcFont:[LcFont br17]];
因禍得福
當這個問題解決的時候,第二個光標刷新的問題也順道解決了,
當然那個問題本質也應該是字體問題,刷新光標的解決方案也只是個trick技巧而已
以上,是昨天遇到的幾個小問題
總結下代碼就是:
第二天更新:
5. 密碼不顯示省略號
今天又有新需求:
如下,當密碼輸入過多的時候,因為顯示不下,所以會顯示"..."
現在是希望,如果是明文密碼,則可以顯示"...",但如果是暗文,則不顯示"..."
開玩笑,這怎么可能,UITextField不是UILabel,不可以設置line breaks, UILabel 可以設置的type如下:
// NSParagraphStyle
typedef NS_ENUM(NSInteger, NSLineBreakMode) {
NSLineBreakByWordWrapping = 0, // Wrap at word boundaries, default
NSLineBreakByCharWrapping, // Wrap at character boundaries
NSLineBreakByClipping, // Simply clip
NSLineBreakByTruncatingHead, // Truncate at head of line: "...wxyz"
NSLineBreakByTruncatingTail, // Truncate at tail of line: "abcd..."
NSLineBreakByTruncatingMiddle // Truncate middle of line: "ab...yz"
} NS_ENUM_AVAILABLE(10_0, 6_0);
但UITexField沒有這個屬性,查了半天,也是相關UITexField搞不定的用UITextView,但那是適用于多行顯示的問題,與本問題無關.
幾經周折("人和人的區別就在這里...")
終于搞定.
那怎么解決呢?
解決方案:
根據textfield的text的內容動態調整期frame,這樣就不會出現"..."了 (出現截斷的本質是因為width不夠,那就增加寬度就好)
當然修改text的frame,這個方法隱藏的比較深了...
- (CGRect)textRectForBounds:(CGRect)bounds {
// Not show truncation text for secureTextEntry,like ****...
if (self.text.length > 0) {
if (self.secureTextEntry && ![self isFirstResponder]) {
CGRect rect = bounds;
CGSize textSize = [self.text sizeOfFont:self.font andWidth:CGFLOAT_MAX];
rect.size.width = MAX(textSize.width, bounds.size.width);
return rect;
}
}
return [super textRectForBounds:bounds];
}
解釋下:
- (CGRect)textRectForBounds:(CGRect)bounds:
lets you set the rectangle for the text when the text field is not being edited.
- (CGRect)editingRectForBounds:(CGRect)bounds:
lets you set the rectangle for the text when the text field is being edited.
// 重寫繪制行為
# 除了UITextField對象的風格選項,
# 你還可以定制化UITextField對象
# 為他添加許多不同的重寫方法,來改變文本字段的顯示行為。
# 這些方法都會返回一個CGRect結構
# 制定了文本字段每個部件的邊界范圍。以下方法都可以重寫。
– textRectForBounds: //重寫來重置文字區域
– drawTextInRect: //改變繪文字屬性.
//重寫時調用super可以按默認圖形屬性繪制,若自己完全重寫繪制函數,就不用調用super了.
– placeholderRectForBounds: //重寫來重置占位符區域
– drawPlaceholderInRect: //重寫改變繪制占位符屬性.
//重寫時調用super可以按默認圖形屬性繪制,若自己完全重寫繪制函數,就不用調用super了.
– borderRectForBounds: //重寫來重置邊緣區域
– editingRectForBounds: //重寫來重置編輯區域
– clearButtonRectForBounds: //重寫來重置clearButton位置,改變size可能導致button的圖片失真
– leftViewRectForBounds:
– rightViewRectForBounds:
結果如下:
參照 http://stackoverflow.com/questions/3287312
第三天更新:
6. placeholder 的字體問題
6.1 背景:
大家不知是否記得我們的第四條字體問題,為了修改字體,使用了如下方法:
[self.textField setFont:nil];
[self.textField setLcFont:[LcFont br17]];
這個本來安心的交給測試了,結果產生了新的問題:
6.2 現象:
這個方法會改變placeholder的字體:
默認情況:
當什么都不輸入,直接點擊眼睛:
(即執行如上setLcFont設置字體的操作之后)
大家可以看到"6-20 characters" 這個字符變小了.
至于為什么會發生這個,很顯然是個蘋果的bug,
首先通過先設置為nil再設置字體的方式,本身就很詭異,自然會引發出新的問題.
目前表現就是placeholder的字體不對了
6.3 那怎么解決呢
那我們就看下怎么修改placeholder的字體,
6.3.1 attributeString
大概看了下,常見思路就是,設置attributestring,這個看上去可以,但是得把代碼中所有的都這么改,而且attribute本身設置起來--"又臭又長",不太實用
所以pass.
6.3.2 placeholderLabel
placeholder 本質是個label,
有個比較常見的設置placeholder color的方法:
[textfield setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
當然這個函數也直接可以通過xcode進行設置完成.
但這個有人反饋自己因為這個被APPSTORE拒絕過.
所以還是pass
6.3.3 自己來渲染
6.3.3.1 textRectForBounds啟發
昨天有介紹過textRectForBounds這個方法,那自然對應也有placeholder相關的方法:
/*
Draws the receiver’s placeholder text in the specified rectangle.
You should not call this method directly.
If you want to customize the drawing behavior for the placeholder text,
you can override this method to do your drawing.
By the time this method is called, the current graphics context is already configured with the default environment and text color for drawing.
In your overridden method, you can configure the current context further and then invoke super to do the actual drawing or do the drawing yourself.
If you do render the text yourself, you should not invoke super.
*/
- (void)drawPlaceholderInRect:(CGRect)rect {}
這個如果你完全自己定義placeholder的text,那就不需要調用super方法,否則建議不然忘記super方法.
6.3.3.2 實現drawPlaceholderInRect
show me the code:
- (void)drawPlaceholderInRect:(CGRect)rect {
//draw place holder.
[[self placeholder] drawInRect:rect withFont:[UIFont systemFontOfSize:17]];
}
okey,這樣一來,字體就保證了,但是run起來會發現,字體的顏色和位置不對了 ---- 始終處于置頂的感覺
6.3.3.3 解決對齊問題
顏色的問題好解決,我們先來看看置頂的問題:
-
調整垂直對齊方式
如下:
self.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
發現無效. 然后依次設置了如下全部ContentVerticalAlignment對齊方式后,只有AlignmentBottom會置底,其他都會置頂.
typedef NS_ENUM(NSInteger, UIControlContentVerticalAlignment) {
UIControlContentVerticalAlignmentCenter = 0,
UIControlContentVerticalAlignmentTop = 1,
UIControlContentVerticalAlignmentBottom = 2,
UIControlContentVerticalAlignmentFill = 3,
};
-
drawInRect
前面的不行,那只能從drawInRect下手了,這里斷點查了下rect如下:
print rect
截圖可以看到y = 0,所以導致無法居中,猜想super的
drawPlaceholderInRect方法里面應該是做了一些對齊的操作,那我們也自己來實現一下吧,這個倒是不難
CGSize size = [self.placeholder sizeOfFont:self.font andWidth:CGFLOAT_MAX];
CGRect placeholderRect =
CGRectMake(rect.origin.x, (rect.size.height - size.height) / 2,
rect.size.width, size.height);
參照http://stackoverflow.com/questions/19093604
-
還差一點
在‘2’的基礎上測試之后,發現位置是終于居中了,但是點擊"eye" buton的時候,位置還是會稍微的有一點點偏移,查了下代碼,如果rect會有改變,應該是UITextfied在你編輯狀態和非編輯狀態的時候,位置會有一定的出入(這個稍微留心點,都能發現)
所以我們的代碼還需要再做一丁點的改變:
CGRect placeholderRect =
CGRectMake(rect.origin.x, (self.height - size.height) / 2,
rect.size.width, size.height);
再測試一下,okey.
6.3.3.3 解決顏色問題
這個沒什么方法,扣色調整下就行,不過整體扣色還是不太準確,需要自己再去調試.
比較常見的顏色是
[UIColor colorWithWhite:0.70 alpha:1]
不過我試下來,感覺還是不對,自己調整了,大概顏色是:
[UIColor colorWithHexString:@"CDCDC8"] colorWithAlphaComponent:0.8]
不過這個不是重點了,找自己家里的UI去做就好了...