在 SecureField 的 SwiftUI 中切换 isSecureTextEntry

swe*_*iya 2 swiftui securefield

我想实现在 SecureField 中显示和隐藏密码的功能。下面是 SecureField 的代码,我添加了按钮,将检测到该按钮以显示和隐藏 SecureField 中的文本。但是 swiftUI 没有类似于isSecureTextEntry 的功能 除了在 Textfield 和 SecureField 之间切换之外还有其他方法

SecureField(placeholder, text: $textValue, onCommit: {
    onReturn?()
})
.onTapGesture {
    onBegin?()
}
.keyboardType(keyboardType)
.font(Font.label.bodyDefault)
.disableAutocorrection(true)
.frame(maxWidth: .infinity, maxHeight: 40)
.padding(.leading)
Run Code Online (Sandbox Code Playgroud)

Yrb*_*Yrb 6

这并不像应有的那么简单。其中一个答案非常接近,但它并不能完全涵盖所有内容,因为它的@FocusState. iOS 15 确实让这个字段更容易构建,因为有两个关键:1. using.opacity()显示和隐藏字段,2. using@FocusState在两个字段之间进行无缝过渡。此方法的唯一缺点是为了实现无缝转换,您必须将键盘限制为仅兼容 ASCII 的字符,因此某些语言被排除在外,例如俄语。

以下代码已更新,以解决评论中提出的 SecureField 重置问题。

struct CustomSecureField: View {
    
    @FocusState private var focused: focusedField?
    @State private var showPassword: Bool = false
    @State private var internalPassword: String
    @State private var keepInternalPassword = false
    @Binding var password: String
    
    init(password: Binding<String>) {
        _internalPassword = State(initialValue: password.wrappedValue)
        _password = password
    }
    
    var body: some View {
        HStack {
            ZStack(alignment: .trailing) {
                TextField("Password", text: $internalPassword)
                    .focused($focused, equals: .unSecure)
                    .autocapitalization(.none)
                    .disableAutocorrection(true)
                // This is needed to remove suggestion bar, otherwise swapping between
                // fields will change keyboard height and be distracting to user.
                    .keyboardType(.alphabet)
                    .opacity(showPassword ? 1 : 0)
                SecureField("Password", text: $internalPassword)
                    .focused($focused, equals: .secure)
                    .autocapitalization(.none)
                    .disableAutocorrection(true)
                    .opacity(showPassword ? 0 : 1)
                Button(action: {
                    showPassword.toggle()
                    focused = focused == .secure ? .unSecure : .secure
                }, label: {
                    Image(systemName: self.showPassword ? "eye.slash.fill" : "eye.fill")
                        .padding()
                })
                .onChange(of: focused, initial: false) { oldValue, newValue in
                    // This prevents sets the value for keepInternalPassword after a focus change
                    // Both values need to be compared because we need to insure the value was
                    // changed from .unsecure
                    if newValue == .secure && oldValue == .unSecure {
                        keepInternalPassword = true
                        print("focus onchange keepInternalPassword = true")
                    }
                }
                .onChange(of: internalPassword, initial: false) { oldValue, newValue in
                    // if the old value is being kept, the internal password is reset
                    // to that value here
                    if keepInternalPassword {
                        DispatchQueue.main.async {
                            keepInternalPassword = false
                            internalPassword = oldValue
                        }
                        return
                    }
                    // otherwise, update password
                    password = internalPassword
                }
            }
        }
    }
    // Using the enum makes the code clear as to what field is focused.
    enum focusedField {
        case secure, unSecure
    }
}
Run Code Online (Sandbox Code Playgroud)