带SecureTextEntry切换功能的UITextField如何设置安全区域避免文本遮挡
问题原因分析
- 文本遮挡按钮:你没有手动调整UITextField的文本可编辑区域边距,且直接手动计算按钮的x坐标,导致按钮位置超出了系统预留的rightView区域,文本编辑区域没有避开按钮位置。
- 图标位置偏右/出现重复图标:调用
enablePasswordToggle时UITextField自身的frame可能尚未完成布局,拿到的宽度不准确,手动设置frame的方式无法响应后续textField的尺寸变化;另外如果多次调用enablePasswordToggle也会重复添加按钮导致出现两个图标。
你遇到的异常效果如下:
修复后的实现代码
import UIKit private var kToggleButtonKey: UInt8 = 0 extension UITextField { // 关联对象存储切换按钮,避免重复添加 private var passwordToggleButton: UIButton? { get { objc_getAssociatedObject(self, &kToggleButtonKey) as? UIButton } set { objc_setAssociatedObject(self, &kToggleButtonKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } fileprivate func setPasswordToggleImage(_ button: UIButton) { let imageName = isSecureTextEntry ? "eye-active" : "eye-inactive" button.setImage(UIImage(named: imageName), for: .normal) // 给按钮加内边距,避免图标贴边 button.imageEdgeInsets = UIEdgeInsets(top: 0, left: -8, bottom: 0, right: 8) } func enablePasswordToggle(){ // 避免重复添加按钮 guard passwordToggleButton == nil else { return } let button = UIButton(type: .custom) setPasswordToggleImage(button) // 只设置尺寸,位置由系统自动管理 button.frame = CGRect(x: 0, y: 0, width: 33, height: 25) button.addTarget(self, action: #selector(togglePasswordView), for: .touchUpInside) self.rightView = button self.rightViewMode = .always self.passwordToggleButton = button } @IBAction func togglePasswordView(_ sender: UIButton) { self.isSecureTextEntry.toggle() setPasswordToggleImage(sender) // 修复切换secureTextEntry时的布局异常 if let existingText = text, isFirstResponder { deleteBackward() insertText(existingText) } } // 调整文本显示区域,避开右侧按钮 open override func textRect(forBounds bounds: CGRect) -> CGRect { var rect = super.textRect(forBounds: bounds) if rightViewMode != .never, let _ = rightView { rect.size.width -= 33 } return rect } // 调整编辑状态下的文本区域,避开右侧按钮 open override func editingRect(forBounds bounds: CGRect) -> CGRect { var rect = super.editingRect(forBounds: bounds) if rightViewMode != .never, let _ = rightView { rect.size.width -= 33 } return rect } // 调整rightView的位置,避免偏右 open override func rightViewRect(forBounds bounds: CGRect) -> CGRect { var rect = super.rightViewRect(forBounds: bounds) rect.origin.x -= 8 return rect } }
修复说明
- 用关联对象存储密码切换按钮,调用
enablePasswordToggle时先判断是否已经存在按钮,避免重复添加导致多图标问题 - 不再手动计算按钮的x坐标,由系统自动排布rightView的位置,重写
rightViewRect(forBounds:)微调按钮的水平位置,符合视觉预期 - 重写
textRect和editingRect方法,给文本显示/编辑区域的右侧留出33pt的边距,完全避开按钮区域,不会出现文本遮挡问题 - 切换
isSecureTextEntry后主动刷新文本输入状态,解决系统原生secureEntry切换时的布局异常、文本错位问题 - 给按钮添加
imageEdgeInsets,避免图标和textField边缘贴得过近,视觉效果更协调
注意:如果你的项目中其他UITextField不需要统一修改边距,可以把重写
textRect、editingRect、rightViewRect的逻辑放到自定义的密码输入框子类中,避免影响全局UITextField的布局。
内容的提问来源于stack exchange,提问作者user17459204
相关产品推荐
相关产品推荐

