2018-5-17重點補充
在我在本身的項目中嘗試用下面的解決方案的時候,發現項目真的是「中毒太深」。在項目中添加擴展以後,發現有的UITextField文字能夠垂直居中了,有的卻仍是不能。真是一臉懵逼啊!!!通過逐行對比代碼發現如下重要規律:git
當UITextField的clipsToBounds = YES或者 layer.masksToBounds = YES的時候,即便加上我下面的擴展也是無法解決問題的,文字仍是沒法垂直居中。親測,不將這兩個屬性設置爲YES的同時,添加該擴展能夠解決問題。github
當UITextField的clipsToBounds = YES或者 layer.masksToBounds = YES的時候輸入文字超長時文字不會超過文本框的範圍。而設置爲NO的話,輸入超長文本的時候的效果如微信和淘寶的搜索框,輸入過程當中會有個超出文本框範圍的動畫,不過這個效果比文本不垂直居中的效果更容易讓人接受。微信
最近發現UITextField在iOS 10下輸入中文的過程當中,文字顯示會「掉下去」或者「文字下沉」,也不知道該怎麼描述了(就是UITextField文字沒有垂直居中),正好個人手機仍是iOS 10的系統,一圖勝千言,下面上效果:網絡
咱們看到,簡書APP、美團APP和優酷APP都有這種問題,除此以外還有不少APP有這種狀況就不一一列舉了,固然還有咱們本身的APP😂。測試
經測試,iOS9和iOS 11沒有這種問題,再加上網上搜了一下基本肯定是iOS 10的bug,但奇怪的是iPhone自帶的APP裏面的UITextField居然沒有這種問題,蘋果爸爸可真是坑得一逼啊🙄。動畫
先讓咱們看看究竟是什麼在搗鬼,先經過Debug View Hierarchy來看下UITextField在輸入狀態下,視圖層級是怎麼樣的:spa
經過上圖咱們能夠看到UITextField在輸入狀態下,上面有一個UIFieldEditor視圖,它是一個UIScrollView的子類。(這裏補充一下,UITextField在未輸入狀態時也就是鍵盤收起時它上面有一個UITextFieldLabel用來展現內容。在輸入狀態時它上面會有一個UIFieldEditor視圖,能夠左右滾動。)咱們還看到這個UIFieldEditor的_UIFiedEditorContentView在垂直方向上和UITextField發生了一點點錯位,問題就出在這裏。3d
咱們來看下不一樣系統下,這個UIFieldEditor的差別在哪裏:code
//iOS9 中文 <UIFieldEditor: 0x7f853e85e800; frame = (0 0; 253 28); text = '撒開發和科技啊話費卡很舒服大方仍是咖啡和卡卡發貨啊...'; clipsToBounds = YES; opaque = NO; gestureRecognizers = <NSArray: 0x7f853dd90af0>; layer = <CALayer: 0x7f853dd728e0>; contentOffset: {7902, 0}; contentSize: {8155, 28}> //iOS 11 中文 <UIFieldEditor: 0x7fad4b024a00; frame = (0 0; 253 28); text = '法卡身份卡上開發開始發售克服恐懼啊身份卡身份卡是否...'; opaque = NO; gestureRecognizers = <NSArray: 0x60000024be20>; layer = <CALayer: 0x600000032c00>; contentOffset: {6840, 0}; contentSize: {7101, 36.5}; adjustedContentInset: {0, 0, 0, 0}> //iOS 10 全英文 <UIFieldEditor: 0x7fd641001600; frame = (0 0; 280 28); text = 'Ghghdghdhddgdgdgdgdgfdgfd...'; clipsToBounds = YES; opaque = NO; gestureRecognizers = <NSArray: 0x600000056f20>; layer = <CALayer: 0x60000002ac40>; contentOffset: {1404.5, 0}; contentSize: {1686, 28}> //iOS 10 中文 <UIFieldEditor: 0x7feebe834a00; frame = (28 0; 225 28); text = '啥開始分開哈薩克仍是個哈時光撒個哈是個好撒個撒個很...'; opaque = NO; gestureRecognizers = <NSArray: 0x60000005ac70>; layer = <CALayer: 0x600000029ae0>; contentOffset: {620, -3}; contentSize: {846, 28}>
注意觀察contentOffset屬性,在iOS 9和iOS 11以及iOS 10輸入全英文字符時contentOffset.y = 0,而在iOS 10輸入全中文時contentOffset.y = -3,是否是有種日了狗的感受?blog
到此,問題的根源算是找到了,那怎麼比較合理地解決這個bug呢?若是你曾經遇到過這個問題而且在網絡上搜索過解決方案,你或許會搜索下面的解決方案:
//重寫UITextField,而後重寫如下方法: - (CGRect)textRectForBounds:(CGRect)bounds { return CGRectInset(bounds, 2, 1); } - (CGRect)editingRectForBounds:(CGRect)bounds { return CGRectInset(bounds, 2, 1); }
這種方式可能會讓文字下沉效果不是那麼明顯,可是這些數字從哪來的呢,沒有任何理論和數據支撐啊,就這樣粗糙的解決不免會留下坑。並且提到UITextField的bug,咱們確定會想到UISearchBar吧?咱們看下UISearchBar的效果:
一樣的問題啊!畢竟UISearchBar上面放着一個UITextField,並且若是咱們用到UISearchDisplayController或者UISearchController,那咱們是否是也要挨個改一遍呢?
顯然這種方式是不可取的,這時候咱們就須要利用runtime了。咱們只須要利用runtime交換掉UITextField的layoutSubViews方法,在交換後的方法裏面經過遍歷子視圖拿到這個UIFieldEditor,而後強行把它的contentOffset.y改爲0就能夠了。由於咱們發如今iOS9和iOS 11以及iOS 10輸入全英文字符狀態下它顯示正常時就是contentOffset.y = 0,這是有理論和數據支撐的。而後,考慮到這個問題是針對iOS 10系統下的,避免對其餘系統或者新系統(好比系統哪天升級到iOS 12,系統對UITextField進行了重構)形成潛在影響,咱們能夠保守一點,只針對iOS 10系統時交換方法。
代碼以下:
// // UITextField+Fix.m // // // Created by 簡書Code_Ninja // Copyright https://github.com/lqcjdx // #import "UITextField+Fix.h" #import <objc/runtime.h> @implementation UITextField (Fix) void swizzleMethod(Class class,SEL originalSelector,SEL swizzledSelector){ Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if(didAddMethod){ class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); }else{ method_exchangeImplementations(originalMethod, swizzledMethod); } } + (void)load { CGFloat systemVersion = [[UIDevice currentDevice].systemVersion floatValue]; if(systemVersion >= 10.0 && systemVersion < 11.0){ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; swizzleMethod(class, @selector(layoutSubviews), @selector(yl_layoutSubviews)); }); } } - (void)yl_layoutSubviews { [self yl_layoutSubviews]; for(UIScrollView *view in self.subviews){ if([view isKindOfClass:[UIScrollView class]]){ CGPoint offset = view.contentOffset; if(offset.y != 0) { offset.y = 0; view.contentOffset = offset; } break; } } } @end
這樣的好處是很是方便,無需修改已有代碼,只需在項目中新建一個UITextField分類就能夠一鍵修復UITextField、UISearchBar、UISearchDisplayController、UISearchController的輸入中文時文字下沉的問題。
修改後的效果:
好了,就寫到這裏了,看完記得去你的項目中看看在iOS 10系統下有沒有這個問題哦。若是你有其餘解決方案歡迎留言。