Swift:设置 isSecureTextEntry 后 TextField 自行清除

Swift: TextField clearing itself after isSecureTextEntry set

我在 SwiftUI 中实现自定义文本字段时遇到一些困难。我正在尝试创建一个密码字段,但是我不得不深入研究 UIKit,因为我需要在字段聚焦时做出反应,并且(与 SwiftUI 中的标准 TextField 不同)没有 onEditingChanged 闭包安全字段。

所以我有以下内容:

struct PasswordField: UIViewRepresentable {

    @ObservedObject var viewModel: TextFieldFloatingWithBorderViewModel
    
    func makeUIView(context: UIViewRepresentableContext<PasswordField>) -> UITextField {
        let tf = UITextField(frame: .zero)
        tf.isUserInteractionEnabled = true
        tf.delegate = context.coordinator
        return tf
    }

    func makeCoordinator() -> PasswordField.Coordinator {
        return Coordinator(viewModel: viewModel)
    }

    func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.text = viewModel.text
        uiView.isSecureTextEntry = !viewModel.isRevealed
    }

    class Coordinator: NSObject, UITextFieldDelegate {
        @ObservedObject var viewModel: TextFieldFloatingWithBorderViewModel
        
        init(viewModel: TextFieldFloatingWithBorderViewModel) {
            self.viewModel = viewModel
        }

        func textFieldDidChangeSelection(_ textField: UITextField) {
            DispatchQueue.main.async {
                self.viewModel.text = textField.text ?? ""
            }
        }

        func textFieldDidBeginEditing(_ textField: UITextField) {
            DispatchQueue.main.async {
                self.viewModel.isFocused = true
            }
        }

        func textFieldDidEndEditing(_ textField: UITextField) {
            DispatchQueue.main.async {
                self.viewModel.isFocused = false
            }
        }

        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
            textField.resignFirstResponder()
            return false
        }
    }
}

然后我们在 SwiftUI 视图中如下声明此字段:

HStack {
    PasswordField(viewModel: viewModel)
    Button(action: {
        viewModel.toggleReveal()
    }) {
        viewModel.revealIcon
            .foregroundColor(viewModel.isFocused ? .blue : viewModel.hasWarning ? .red: .gray)
    }
 }

在 TextFieldFloatingWithBorderViewModel viewModel 中,我们有一个 Published var isRevealed,然后在点击按钮时调用以下方法:

func toggleReveal() {
    isRevealed.toggle()
}

因为在密码字段中我们有:

uiView.isSecureTextEntry = !viewModel.isRevealed

这会在安全视图(即用点屏蔽的输入)和标准视图之间切换密码字段。这运行良好,除了当用户切换回隐藏并继续键入密码时被擦除:

我不明白为什么要在这里擦除密码,而且只有当密码从不安全变为安全时(而不是相反)

您已经有 @FocusState 了。您可以像使用任何状态变量一样使用它。这是我在 TextField 上放置黄色背景并使用 @FocusState 变量有条件地控制它的一些代码:

struct HighlightWhenFocused: View {
    @State private var password: String = ""
    @FocusState var passwordFocused: Bool

    var body: some View {
        PasswordView(password: $password)
            .focused($passwordFocused)
            .background(
                Color.yellow
                    .opacity(passwordFocused ? 1 : 0)
            )
    }
}

struct PasswordView: View {
    
    @Binding var password: String
    @State private var secured = true
    
    var body: some View {
        HStack{
            Text("Password:")
            if secured{
                SecureField("",text:$password)
            }
            else{
                TextField("",text: $password)
            }
            Button {
                secured.toggle()
            } label: {
                Image(systemName: secured ? "eye.slash" : "eye")
            }
            .buttonStyle(BorderlessButtonStyle())
        }
    }
}

对其进行编辑,使其成为安全的密码解决方案。请注意,当您在 viewable/non-viewable 之间切换时,这些字段会失去焦点,因为您点击了它们之外的区域。