I want to fetch and set date from DatePicker
, but my date is not updating. SwiftUI is new to me and I am confused with what type of property wrapper to use. Please help in this and advice when and where to use @State
, @Binding
, @Published
I read some articles but still concept is not clear to me.
Here I used MVVM and SwiftUI and my code as follows.
class MyViewModel:ObservableObject {
@Published var selectedDate : Date = Date()
@Published var selectedDateStr : String = Date().convertDateToString(date: Date())
}
struct DatePickerView: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@ObservedObject var viewModel : MyViewModel
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .long
return formatter
}
@State private var selectedDate = Date()
var body: some View {
VStack {
//Title
HStack{
Text("SELECT A DATE")
.foregroundColor(.white)
.font(.system(size: 20))
}
.frame(width:UIScreen.main.bounds.width,height: 60)
.background(Color.red)
//Date Picker
DatePicker(selection: $selectedDate, in: Date()-15...Date(), displayedComponents: .date) {
Text("")
}.padding(30)
Text("Date is \(selectedDate, formatter: dateFormatter)")
Spacer()
//Bottom buttons
Text("DONE")
.fontWeight(.semibold)
.frame(width:UIScreen.main.bounds.width/2,height: 60)
.onTapGesture {
self.viewModel.selectedDate = self.selectedDate
self.presentationMode.wrappedValue.dismiss()
}
}
}
}
//calling:
DatePickerView(viewModel: self.viewModel)
Reply against your second question about wrapper properties used in SwiftUI i.e @State, @Binding, @Published.
The most common @Things used in SwiftUI are:
• @State - Binding<Value>
• @Binding - Binding<Value>
• @ObservedObject - Binding<Value> (*)
• @EnvironmentObject - Binding<Value> (*)
• @Published - Publisher<Value, Never>
(*) technically, we get an intermediary value of type Wrapper, which turns a Binding once we specify the keyPath to the actual value inside the object. So, as you can see, the majority of the property wrappers in SwiftUI, namely responsible for the view’s state, are being “projected” as Binding, which is used for passing the state between the views. The only wrapper that diverges from the common course is @Published, but: 1. It’s declared in Combine framework, not in SwiftUI 2. It serves a different purpose: making the value observable 3. It is never used for a view’s variable declaration, only inside ObservableObject Consider this pretty common scenario in SwiftUI, where we declare an ObservableObject and use it with @ObservedObject attribute in a view:
class ViewModel: ObservableObject {
@Published var value: Int = 0
}
struct MyView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View { ... }
}
MyView can refer to $viewModel.value and viewModel.$value - both expressions are correct. Quite confusing, isn’t it? These two expressions ultimately represent values of different types: Binding and Publisher, respectively. Both have a practical use:
var body: some View {
OtherView(binding: $viewModel.value) // Binding
.onReceive(viewModel.$value) { value // Publisher
// do something that does not
// require the view update
}
}
Hope it may help you.
You can calculate the current date - 15 days
using this:
let previousDate = Calendar.current.date(byAdding: .day, value: -15, to: Date())!
Then use the previousDate
in DatePicker`s range:
DatePicker(selection: $selectedDate, in: previousDate...Date(), displayedComponents: .date) { ...
Summing up, your code can look like this:
struct DatePickerView: View {
@Environment(\.presentationMode) var presentationMode
@ObservedObject var viewModel: MyViewModel
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .long
return formatter
}
@State private var selectedDate = Date()
let previousDate = Calendar.current.date(byAdding: .day, value: -15, to: Date())!
var body: some View {
VStack {
//Title
HStack{
Text("SELECT A DATE")
.foregroundColor(.white)
.font(.system(size: 20))
}
.frame(width:UIScreen.main.bounds.width,height: 60)
.background(Color.red)
//Date Picker
DatePicker(selection: $selectedDate, in: previousDate...Date(), displayedComponents: .date) {
Text("")
}.padding(30)
Text("Date is \(selectedDate, formatter: dateFormatter)")
Spacer()
//Bottom buttons
Button(action: {
self.viewModel.selectedDate = self.selectedDate
self.presentationMode.wrappedValue.dismiss()
}) {
Text("DONE")
.fontWeight(.semibold)
}
}
}
}
Tested in Xcode 11.5, Swift 5.2.4.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With