記錄一下解決這個問題的過程
在密碼輸入框使用secureTextEntry = YES, 可以避免用戶使用第三方鍵盤或者切換鍵盤類型后輸入中文等奇奇怪怪不應(yīng)該作為密碼的文本,但國內(nèi)大多產(chǎn)品都有個不符合iOS設(shè)計規(guī)范的需求就是用戶可以點擊一個類似眼睛的按鈕顯示密碼明文。
如果直接設(shè)置secureTextEntry = NO,會發(fā)生鍵盤直接切換為第三方鍵盤(iOS 10.3系統(tǒng)下發(fā)生),并且可以切換鍵盤類型,導(dǎo)致用戶輸入不規(guī)范。
嘗試過設(shè)置一個全局變量在密碼輸入框響應(yīng)時
- (BOOL)application:(UIApplication *)application shouldAllowExtensionPointIdentifier:(UIApplicationExtensionPointIdentifier)extensionPointIdentifier {
if ([extensionPointIdentifier isEqualToString:UIApplicationKeyboardExtensionPointIdentifier]) {
return ShouldAllowKeyboardExtension;
}
return YES;
}
系統(tǒng)回調(diào)返回NO,來避免鍵盤直接切換為第三方鍵盤,但這么做有兩個問題:
- 當(dāng)此回調(diào)返回一次YES后,系統(tǒng)就把允許第三方鍵盤使用緩存下來,后續(xù)再返回NO也無效,不能達到每次密碼輸入框響應(yīng)時禁用第三方鍵盤的效果;
- 就算禁用了第三方鍵盤,在secureTextEntry = NO的情況下系統(tǒng)鍵盤可以提供語音輸入,在語音輸入類型下可以切換為普通話再返回鍵盤輸入時可以輸入中文(iOS 12.0系統(tǒng)下京東存在這種情況);
那么結(jié)論就是密碼輸入框的secureTextEntry只能設(shè)置為YES。
接下來就考慮怎么在secureTextEntry = YES情況下顯示明文密碼,網(wǎng)上搜索修改font的辦法無效(iOS 10.3下測試)。
起初嘗試在監(jiān)聽用戶輸入時調(diào)用setNeedsDisplay來重繪,但是這樣涉及的邏輯太多,且還要計算光標位置,太麻煩的方法對我來說不可取,于是換個思路,找找系統(tǒng)是在什么時候使用“點”來繪制文本的。
系統(tǒng)需要用“點”來繪制文本的前提條件是secureTextEntry屬性為YES時,當(dāng)我查看系統(tǒng)調(diào)用這個屬性getter方法時的堆棧信息里發(fā)現(xiàn),在此之前調(diào)用了_shouldObscureInput,哇,這分明就是我想要的東西。
于是繼承UITextFiled重寫_shouldObscureInput后無論返回YES/ NO結(jié)果都不影響,再繼續(xù)分析調(diào)用此屬性時的堆棧發(fā)現(xiàn),在某次調(diào)動該屬性前還調(diào)用了[UITextFiled font]和[UIFiledEditor font]。
猜想可能secureTextEntry的“點”確實和字體有關(guān)(如果要我來做的話我也會這么做),但更關(guān)鍵的點是我發(fā)現(xiàn)內(nèi)容的實際繪制是在UIFieldEditor上,所以我要找的東西也應(yīng)該在這上面。
Class-dump了UIKit.framework,在UIFieldEditor.h里面果然發(fā)現(xiàn)了很多obscureInput相關(guān)方法
- (_Bool)_shouldObscureInput;
- (void)_obscureAllText;
- (void)_cancelObscureAllTextTimer;
- (struct _NSRange)_unobscuredSecureRange;
最后hook了UIFieldEditor的_shouldObscureInput方法返回我想要的值就達到目的啦~
再加一句更新下UITextField.text屬性可以“刷新”UITextField。
以上是在iOS10.3系統(tǒng)的測試機下實現(xiàn)了效果,如果存在兼容問題后續(xù)再更新吧。