Invincible_Pain
Invincible_Pain

Reputation: 571

In SwiftUI how can I use @State and @Binding to deal with different types?

As the title might not very clear, below is an example: I have a View which is just DatePicker, name is "MIMIRxDatePicker"

struct MIMIRxDatePicker: View {
@Binding var dobStr: String
@Binding var screenShouldGrayOut: Bool
    
var body: some View {
    VStack {
        Text("Select Date of Birth: \(dateFormatter.string(from: self.dobStr))")
        DatePicker(selection: self.dobStr , in: ...Date(), displayedComponents: .date) {
           Text("")
        }
        Button(action: {
                withAnimation {
                    self.screenShouldGrayOut.toggle()
                }
            }) {
                Text("Choose").foregroundColor(.white).padding()
                }.background(Color(Constants.ThemeColor)).cornerRadius(20)
        }.cornerRadius(10).frame(width: 270).padding(20).background(Color(.white))
    }
}

Heres is MIMIRxDatePicker s parent view: SignupContentView

struct SignupContentView: View {
    @State var email: String = ""
    @State var firstName: String = ""
    @State var lastName: String = ""
    @State var dobStr: String = ""
    @State var screenShouldGrayOut: Bool = false
    
    var body: some View {
        ZStack {
            Color(Constants.ThemeColor)
            ScrollView(.vertical) {
                Spacer().frame(height: 50)
                VStack (alignment: .center, spacing: 2) {
                    Spacer()
                    Group {
                        Group {
                            HStack {
                                Text("Email:").modifier(AuthTextLabelModifier())
                                Spacer()
                            }.padding(2)
                            
                            HStack {
                                Image(systemName: "envelope").foregroundColor(.white)
                                TextField("Email", text: $email).foregroundColor(.white)
                            }.modifier(AuthTextFieldContainerModifier())
                        }
                        
                        Group {
                            HStack {
                                Text("First Name:").modifier(AuthTextLabelModifier())
                                Spacer()
                            }.padding(2)
                            
                            HStack {
                                Image(systemName: "person").foregroundColor(.white)
                                TextField("First Name", text: $firstName).foregroundColor(.white)
                            }.modifier(AuthTextFieldContainerModifier())
                        }
                        
                        Group {
                            HStack {
                                Text("Last Name:").modifier(AuthTextLabelModifier())
                                Spacer()
                            }.padding(2)
                            HStack {
                                Image(systemName: "person").foregroundColor(.white)
                                TextField("Last Name", text: $lastName).foregroundColor(.white)
                            }.modifier(AuthTextFieldContainerModifier())
                            Spacer()
                            HStack { GenderSelector() }
                        }
                        
                        Group {
                            HStack {
                                Text("Date of Birth:").modifier(AuthTextLabelModifier())
                                Spacer()
                            }.padding(2)
                            HStack {
                                Image(systemName: "calendar").foregroundColor(.white)
                                TextField("", text: $dobStr).foregroundColor(.white).onTapGesture {
                                    
                                    withAnimation {
                                        self.screenShouldGrayOut.toggle()
                                    }
                                }
                            }.modifier(AuthTextFieldContainerModifier())
                        }
                    }
                }.padding()
            }.background(screenShouldGrayOut ? Color(.black) : Color(.clear)).navigationBarTitle("Sign Up", displayMode: .inline)
            if screenShouldGrayOut {
                MIMIRxDatePicker(dobStr: $dobStr, screenShouldGrayOut: $screenShouldGrayOut).animation(.spring())
            }
        }.edgesIgnoringSafeArea(.all)
    }
}

As you can see the @State var dobStr in "SignupContentView" is a String which is Date of birth's TextField needed, but in the MIMIRxDatePicker the DatePicker's selection param needs a type of Date, not a string, even if I declared a @Binding var dobStr: String in MIMIRxDatePicker.

I also tried:

var dateFormatter: DateFormatter {
    let formatter = DateFormatter()
    formatter.dateStyle = .long
    return formatter
}

and do:

DatePicker(selection: dateFormatter.date(from: self.dobStr)  , in: ...Date()....

But it didn't work.

I know that assume if the DatePicker is a TextField then everything will work and good to go because TextField accept String only also, but that's not the case, I need the DatePicker.

So in SwiftUI how can I use @State and @Binding to deal with different types?

Upvotes: 1

Views: 521

Answers (2)

Jim lai
Jim lai

Reputation: 1409

You need to first identify the types involved.

You are passing a Binding<String> to selection: which expects a Binding<Date>.

@Binding var dobStr: String is still Binding<String>

dateFormatter.date(from: self.dobStr) is Date, not Binding<Date>

What you need is to create a custom Binding<Date>, with its getter/setter interacting with Binding<String>

E.g.;

    Binding(
        get: { // return a Date from dobStr binding },
        set: { // set dobStr binding from a Date}
    )

Then use this Binding as argument to selection:

Upvotes: 0

pawello2222
pawello2222

Reputation: 54611

What you can do is to use Date for date manipulation and String for displaying the date.

Which means you can use Date variable in your Picker and String in the Text views.

struct MIMIRxDatePicker: View {
    @State var dob: Date
    ...
    
    let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .long
        return formatter
    }()
    
    var dobStr: String {
        dateFormatter.string(from: self.dob)
    }

    var body: some View {
        VStack {
            // `String` for displaying...
            Text("Select Date of Birth: \(self.dobStr)")
            // and `Date` for date manipulation...
            DatePicker(selection: self.$dob, in: ...Date(), displayedComponents: .date) {
                Text("")
            }
            ...
        }
        ...
    }
}

Then follow the same pattern in your SignupContentView. Generally try to use Date objects for date manipulation as they are less prone to errors and malformed data.

Upvotes: 2

Related Questions