I’m working on a month and year picker, I want them to be separate, and the order of the months is always the same (January, February etc.) I’ve already managed to create this view, but when I select November, for example, it doesn’t change to November.
I hope you can help me, thank you very much in advance.
This is my MonthPicker code:
struct MonthPicker: View {
@ObservedObject var viewModel: CalendarViewModel
var body: some View {
Picker("", selection: $viewModel.selectedDate) {
ForEach(generateMonths(), id: \.self) { monthString in
Text(monthString)
.tag(monthString)
}
}
.labelsHidden()
// Entfernen Sie den datePickerStyle-Modifikator
.background(Color.white)
.cornerRadius(8)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color.blue, lineWidth: 1)
)
.onChange(of: viewModel.selectedDate, perform: { newDate in
// Handle month change here
})
}
private func generateMonths() -> [String] {
let calendar = Calendar.current
let currentDate = Date()
var months: [String] = []
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM"
for i in 0..<12 {
if let date = calendar.date(bySetting: .month, value: i + 1, of: currentDate) {
let monthString = dateFormatter.string(from: date)
months.append(monthString)
}
}
return months
}
}
This is my generateMonths code:
private func generateMonths() -> [String] {
let calendar = Calendar.current
let currentDate = Date()
let startOfYear = calendar.startOfDay(for: currentDate)
var months: [String] = []
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM"
for i in 0..<12 {
if let date = calendar.date(byAdding: .month, value: i, to: startOfYear) {
let monthString = dateFormatter.string(from: date)
months.append(monthString)
}
}
return months
}
And this is how I display the month at the end:
struct test: View {
@StateObject var viewModel = CalendarViewModel()
var body: some View {
HStack (){
Spacer()
MonthPicker(viewModel: viewModel)
.padding()
.labelsHidden()
Spacer()
}
}
}
This is my CalenderViewModel:
class CalendarViewModel: ObservableObject {
@Published var selectedDate: Date = Date()
}
>Solution :
To solve this problem, I suggest you create a dedicated structure to represent a month and use it as the value for the Picker.
- Create a structure for the month: This structure will contain the month name and month number.
- Modify generateMonths to return a list of these structures.
- Update the Picker to use this structure as a tag.
- Ensure that viewModel.selectedDate is correctly updated when the user chooses a month.
—
struct MonthItem: Identifiable {
let id: Int
let name: String
init(month: Int, calendar: Calendar = Calendar.current) {
self.id = month
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM"
if let date = calendar.date(bySetting: .month, value: month, of: Date()) {
self.name = dateFormatter.string(from: date)
} else {
self.name = ""
}
}
}
private func generateMonths() -> [MonthItem] {
return (1...12).map { MonthItem(month: $0) }
}
Picker("", selection: $viewModel.selectedMonth) {
ForEach(generateMonths()) { month in
Text(month.name).tag(month.id)
}
}
.onChange(of: viewModel.selectedMonth) { newMonth in
// Update selectedDate with new selected month
}
class CalendarViewModel: ObservableObject {
@Published var selectedMonth: Int // Here, store the number of the selected month
@Published var selectedDate: Date // Use this date to reflect the selected month and year
}