Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fetching and setting date from datepicker, but still getting old default value

Tags:

swiftui

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)

like image 783
karan singh Avatar asked Mar 02 '23 08:03

karan singh


2 Answers

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.

like image 111
Nidhi Tiwari Avatar answered Mar 10 '23 18:03

Nidhi Tiwari


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.

like image 33
pawello2222 Avatar answered Mar 10 '23 17:03

pawello2222