如何修复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的系统,一图胜千言,下面上效果:

简书.png
美团.png
优酷.png

我们看到,简书APP、美团APP和优酷APP都有这种问题,除此之外还有很多APP有这种情况就不一一列举了,当然还有我们自己的APP😂。

经测试,iOS9和iOS 11没有这种问题,再加上网上搜了一下基本确定是iOS 10的bug,但奇怪的是iPhone自带的APP里面的UITextField竟然没有这种问题,苹果爸爸可真是坑得一逼啊🙄。

先让我们看看到底是什么在捣鬼,先通过Debug View Hierarchy来看下UITextField在输入状态下,视图层级是怎么样的:

UITextField效果.png

通过上图我们可以看到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效果.png

同样的问题啊!毕竟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的输入中文时文字下沉的问题。

修改后的效果:

修改后的效果.png

好了,就写到这里了,看完记得去你的项目中看看在iOS 10系统下有没有这个问题哦。如果你有其他解决方案欢迎留言。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容