You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()
}

关键修复点

  1. 为SecureInputField添加@Environment(\.colorScheme),便于在组件内部获取当前颜色模式
  2. 在makeUIView和updateUIView中,直接根据颜色模式设置textColor,避免系统自动填充时覆盖样式
  3. 在Coordinator的textFieldDidChangeSelection方法中,每次文本变化(包括自动填充)都重置textColor并调用setNeedsDisplay()强制刷新视图,确保密码圆点颜色与背景色形成对比

内容的提问来源于stack exchange,提问作者Bartender1382

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.03 19:04:49