1

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?

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

This will work fine as long as the requirement is one-way relationship from datepicker to Text as is the case here with tapgesture on the text. If coder wants to give the ability to both edit the date as text as well as datepicker, then solution may get complicated with two way binding.
0

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:

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.