SwiftUI自定义SecureField深色模式自动填充密码时圆点消失问题
解决iOS自动填充强密码后深色模式下自定义SecureField密码圆点消失问题
问题描述
通过UIViewRepresentable封装UITextField实现自定义SecureField时,手动输入密码在亮色/深色模式下密码圆点均正常显示;但当iOS建议并自动填充强密码后,深色模式下的密码圆点会消失,切换至亮色模式再切回深色模式后圆点又恢复可见。
原因分析
iOS自动填充密码时,会修改UITextField的内部样式属性,导致密码圆点颜色与背景色混淆;而自动填充完成后未触发视图样式的主动刷新,因此深色模式下圆点不可见。切换颜色模式时会触发SwiftUI的视图更新,重新应用样式,圆点便恢复显示。
修复方案
通过监听文本变化事件,强制重置密码输入框的文本颜色并刷新视图,确保自动填充后样式正确。以下是修复后的完整代码:
import SwiftUI import UIKit let warmMustard = Color(red: 0.780, green: 0.659, blue: 0.290) let lightText = Color(red: 0.973, green: 0.961, blue: 0.933) let darkText = Color(red: 0.118, green: 0.118, blue: 0.118) func fieldBackground(_ scheme: ColorScheme) -> Color { return scheme == .dark ? .black : .white } func fieldText(_ scheme: ColorScheme) -> Color { return scheme == .dark ? .white : .black } struct SecureInputField: UIViewRepresentable { @Binding var text: String var placeholder: String? @Environment(\.colorScheme) var colorScheme func makeUIView(context: Context) -> UITextField { let tf = UITextField() tf.isSecureTextEntry = true tf.placeholder = placeholder tf.text = text tf.textColor = colorScheme == .dark ? .white : .black tf.borderStyle = .none tf.autocorrectionType = .no tf.autocapitalizationType = .none tf.delegate = context.coordinator context.coordinator.observe(tf) return tf } func updateUIView(_ uiView: UITextField, context: Context) { if uiView.text != text { uiView.text = text } uiView.isSecureTextEntry = true uiView.textColor = colorScheme == .dark ? .white : .black uiView.setNeedsDisplay() } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UITextFieldDelegate { var parent: SecureInputField var observation: NSKeyValueObservation? init(_ parent: SecureInputField) { self.parent = parent } func observe(_ tf: UITextField) { observation = tf.observe(\.backgroundColor, options: [.new]) { [weak tf] _, _ in DispatchQueue.main.async { tf?.backgroundColor = .clear } } } func textFieldDidChangeSelection(_ textField: UITextField) { parent.text = textField.text ?? "" // 文本变化时重置颜色并刷新视图 textField.textColor = parent.colorScheme == .dark ? .white : .black textField.setNeedsDisplay() } } } struct SecureFieldTestView: View { @Environment(\.colorScheme) var colorScheme @State private var username = "" @State private var password = "" @State private var confirmPassword = "" var body: some View { ZStack { Color(colorScheme == .dark ? UIColor.black : UIColor.white) .ignoresSafeArea() VStack(spacing: 20) { Text("Dark mode dot reproduction") .foregroundColor(fieldText(colorScheme)) TextField("Username", text: $username) .textContentType(.username) .autocorrectionDisabled() .textInputAutocapitalization(.never) .padding() .background(fieldBackground(colorScheme)) .cornerRadius(8) .foregroundColor(fieldText(colorScheme)) .overlay(RoundedRectangle(cornerRadius: 8).stroke(warmMustard, lineWidth: 2)) SecureInputField(text: $password, placeholder: "Password") .id("password") .frame(height: 24) .padding() .background(fieldBackground(colorScheme)) .cornerRadius(8) .overlay(RoundedRectangle(cornerRadius: 8).stroke(warmMustard, lineWidth: 2)) SecureInputField(text: $confirmPassword, placeholder: "Confirm Password") .id("confirmPassword") .frame(height: 24) .padding() .background(fieldBackground(colorScheme)) .cornerRadius(8) .overlay(RoundedRectangle(cornerRadius: 8).stroke(warmMustard, lineWidth: 2)) } .padding(.horizontal, 32) } } } #Preview { SecureFieldTestView() }
关键修复点
- 为
SecureInputField添加@Environment(\.colorScheme),便于在组件内部获取当前颜色模式 - 在
makeUIView和updateUIView中,直接根据颜色模式设置textColor,避免系统自动填充时覆盖样式 - 在
Coordinator的textFieldDidChangeSelection方法中,每次文本变化(包括自动填充)都重置textColor并调用setNeedsDisplay()强制刷新视图,确保密码圆点颜色与背景色形成对比
内容的提问来源于stack exchange,提问作者Bartender1382
相关产品推荐
相关产品推荐

