iOS15下isSecureTextEntry为true的UITextField赋值Unicode字符异常
问题根因
iOS15对isSecureTextEntry = YES的UITextField渲染逻辑做了变更:iOS14及更低版本中,安全输入框会自动忽略U+200B零宽空格这类不可见Unicode字符,不会将其识别为有效输入渲染为密码圆点;iOS15及之后所有赋值给安全输入框的Unicode字符都会被判定为有效输入,无论字符本身是否可见,都会被渲染为密码圆点,这就是你遇到的异常表现的原因。
解决方案
以下方案按优先级排序推荐:
方案1:重构退格监听逻辑,彻底弃用零宽字符(最优解)
你插入零宽字符的核心诉求是解决空UITextField无法触发退格(deleteBackward)回调的问题,通过自定义UITextField重写退格方法即可实现相同能力,不需要插入任何占位字符,从根源上规避系统版本差异:
// OTPTextField.h @interface OTPTextField : UITextField @property (nonatomic, copy) void (^deleteBackwardCallback)(void); @end // OTPTextField.m @implementation OTPTextField - (void)deleteBackward { [super deleteBackward]; if (self.deleteBackwardCallback) { self.deleteBackwardCallback(); } } @end
使用该自定义类替换原有UITextField即可,成为第一响应者时不需要插入零宽字符,退格操作会直接触发回调,你可以在回调中处理前一个输入框跳转、清空等原有逻辑。
方案2:兼容原有逻辑,iOS15+特殊渲染零宽字符
如果不想改动现有业务逻辑,可以通过富文本设置零宽字符的属性,避免系统渲染出密码圆点:
if (@available(iOS 15.0, *)) { if (textField.isSecureTextEntry) { NSAttributedString *zeroWidthAttr = [[NSAttributedString alloc] initWithString:@"" attributes:@{ NSFontAttributeName: [UIFont systemFontOfSize:0], NSForegroundColorAttributeName: UIColor.clearColor }]; textField.attributedText = zeroWidthAttr; return; } } // 低于iOS15版本走原有逻辑 textField.text = @"";
方案3:代理拦截临时插入零宽字符
实现UITextField的代理方法,仅在退格逻辑需要时临时插入零宽字符,处理完成后立即清空:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { if (@available(iOS 15.0, *) && textField.isSecureTextEntry) { if ([string isEqualToString:@""]) { // 临时插入用于触发退格回调,10ms后自动清空 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{ if ([textField.text isEqualToString:@""]) { textField.text = nil; } }); } } return YES; }
内容的提问来源于stack exchange,提问作者Krima Shah
相关产品推荐
相关产品推荐

