1
votes

I am new to SwiftUI. I wanted to develop a custom TextField which accepts a parameter for changing itself as a secure field with show and hide password facility. But I am stuck with an issue now. As the caller of my custom textfield will pass a State variable as parameter, i have to use @Binding here inside my class. But when i use binding, the text gets disappear while clicking the eye icon. Kindly correct me if my assumptions described above or coded below has any mistakes.

import SwiftUI

struct BTextInput: View {
    @Binding var valueHolder : String
    var hint : String?
    @State var isSecure: Bool?
    @State var isTextHidden: Bool = true

    var body: some View {
        HStack{
            if isSecure ?? false{

                if isTextHidden {
                    VStack{
                        SecureField("\(hint.optionalVal)" ,text : self.$valueHolder)
                            .lineLimit(1)
                            .multilineTextAlignment(.leading)
                            .padding(.horizontal,4)
                            .padding(.trailing,28)

                        Rectangle().frame(height: 0.5)
                            .foregroundColor(.gray)
                    }.overlay(
                        Button(action: {
                            self.isTextHidden.toggle()
                        }){
                            EyeImage(name: "eye")
                        }.padding(.top,2.5)
                        ,alignment: .topTrailing)
                }else{
                    VStack{
                        TextField("\(hint.optionalVal)" ,text : self.$valueHolder)
                            .lineLimit(1)
                            .multilineTextAlignment(.leading)
                            .padding(.horizontal,4)
                            .padding(.trailing,28)

                        Rectangle().frame(height: 0.5)
                            .foregroundColor(.gray)
                    }.overlay(
                        Button(action: {
                            self.isTextHidden.toggle()
                        }){
                            EyeImage(name: "eye.slash")
                        }.padding(.top,2.5)
                        ,alignment: .topTrailing)
                }
           }else{
                VStack{
                    TextField("\(hint.optionalVal)" ,text : self.$valueHolder)
                        .lineLimit(1)
                        .multilineTextAlignment(.leading)
                        .padding(.horizontal,4)

                    Rectangle().frame(height: 0.5)
                        .foregroundColor(.gray)
                }
            }
        }.padding()
    }
}
struct EyeImage: View {

    private var imageName: String
    init(name: String) {
        self.imageName = name
    }
    var body: some View {
        Image(systemName : imageName)
            .foregroundColor(.black)
    }
}

struct BTextInput_Previews: PreviewProvider {
    static var previews: some View {
        VStack{
            BTextInput(valueHolder: Binding.constant(""), hint: "Account")
            BTextInput(valueHolder: Binding.constant(""), hint: "Account", isSecure: true)
        }
    }
}

This code will work as expected if we change the '@Binding var valueHolder : String' as '@State var valueHolder : String'. But I need to keep it as a binding variable itself as this is a custom TextInput.

It will be appreciable if you can suggest any better approach to achieve the same. (Also the mistakes in my code / better coding practises) Thanks in advance

1

1 Answers

0
votes

If not take into account lost focus in visibility change, all works fine. I assume you confused by using constant binding in Preview, because binding, actually, is passive - it is state responsible for update. So to preview component dependent on binding it is needed a helper view having active state, like below:

struct BTextInput_Previews: PreviewProvider {
    struct TestSecureFieldSwitch: View {
        @State private var text = ""
        @State private var pass = ""
        var body: some View {
            VStack{
                BTextInput(valueHolder: $text, hint: "Account")
                BTextInput(valueHolder: $pass, hint: "Password", isSecure: true)
            }
        }
    }

    static var previews: some View {
        TestSecureFieldSwitch()
    }
}

Tested with Xcode 11.4 / iOS 13.4