如何修復UITextField在iOS10文字下沉問題
2018-5-17重點補充
在我在自己的專案中嘗試用下面的解決方案的時候,發現專案真的是“中毒太深”。在專案中新增擴充套件之後,發現有的UITextField文字可以垂直居中了,有的卻還是不能。真是一臉懵逼啊!!!經過逐行對比程式碼發現以下重要規律:
當UITextField的clipsToBounds = YES或者 layer.masksToBounds = YES的時候,即使加上我下面的擴充套件也是沒法解決問題的,文字還是無法垂直居中。親測,不將這兩個屬性設定為YES的同時,新增該擴充套件可以解決問題。
當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在輸入狀態下,檢視層級是怎麼樣的:
通過上圖我們可以看到UITextField在輸入狀態下,上面有一個UIFieldEditor檢視,它是一個UIScrollView的子類。(這裡補充一下,UITextField在未輸入狀態時也就是鍵盤收起時它上面有一個UITextFieldLabel用來展示內容。在輸入狀態時它上面會有一個UIFieldEditor檢視,可以左右滾動。)我們還看到這個UIFieldEditor的_UIFiedEditorContentView在垂直方向上和UITextField發生了一點點錯位,問題就出在這裡。
我們來看下不同系統下,這個UIFieldEditor的差異在哪裡:
//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,是不是有種日了狗的感覺?
到此,問題的根源算是找到了,那怎麼比較合理地解決這個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系統下有沒有這個問題哦。如果你有其他解決方案歡迎留言。
相關文章
- 如何修復Vue中的 “this is undefined” 問題VueUndefined
- GRPC使用問題修復RPC
- Oracle日常問題-壞塊修復Oracle
- sqlServer修復有問題的表SQLServer
- win10 如何修復game bar win10game bar出現問題怎麼修復Win10GAM
- 如何修復mac電腦藍芽不可用的問題Mac藍芽
- 啟動/刪除Docker容器時出現問題 - 如何修復Docker
- 如何在Mac上執行修復Safari緩慢的問題?Mac
- 如何修復windows桌面資料夾變黑圖示問題Windows
- 如何修復Apple AirPods Max電池耗盡問題?APPAI
- 如何修復 Ubuntu 中檢測到系統程式錯誤的問題Ubuntu
- PrestaShop網站漏洞修復如何修復REST網站
- 使用git修復線上指定版本的問題Git
- 配置dg broker的問題分析及修復
- Jtti:如何修復Oracle資料庫執行過程的問題JttiOracle資料庫
- 我的網站被黑了,該如何排除漏洞並修復安全問題網站
- 修復 SSL Certificate Problem,如何定位及常見問題的處理策略
- [iOS]適配iOS10問題iOS
- Windows啟動問題修復(重建活動分割槽)Windows
- 修復SSH在 MacOS Ventura 系統上不能使用RSA簽名的問題Mac
- iOS10更易遭暴力破解,蘋果稱已開始修復iOS蘋果
- iOS UITextField實時監測編輯的文字iOSUI
- [修復Win8.1 BUG] 解決Win8.1英文字型發虛不渲染問題
- Chrome更新來襲,共修復7個安全問題Chrome
- 修復UEFI模式下Manjaro Linux啟動問題模式JARLinux
- 運維常見軟體問題排查與修復運維
- 二進位制修復中文亂碼的問題
- CocoaPods使用及安裝常見問題修復
- Oracle分割槽資料問題的分析和修復Oracle
- .NET Core 2.0遷移技巧之MemoryCache問題修復
- 如何解決macOS在 Finder裡不可以新建txt文字的問題?Mac
- 聊聊如何修復springboot使maven-resources-plugin佔位符失效問題Spring BootMavenPlugin
- 修正BlogEngine.Net中的使用全中文為標題的連結問題(在1.1以上版本此問題已被修復)...
- 安全模式如何修復電腦 開機如何進入系統修復模式
- Mathtype 公式在正文中高於文字的問題公式
- [風險預警]Go 1.13.2和1.12.11會在17號左右更新修復安全問題Go
- 微軟將在9月中旬修復CPU利用率飆高的問題微軟
- 谷歌改口,將修復地理位置資訊洩露問題谷歌