New in SwiftUI 4: MultiDatePicker

DevTechie Inc
Jun 24, 2022

SwiftUI 4 came with another cool new addition, MultiDatePicker. This new control allows us to pick multiple dates.

Let’s get started with an example. Creating MultiDatePicker is easy, all we need is a State variable to store selected dates and MultiDatePicker view itself.

State variable os passed as selection binding value into the MutiDatePicker as shown below:

struct ContentView: View {
    
    @State private var selectedDates: Set<DateComponents> = []
    
    var body: some View {
        VStack {
            Text("DevTechie!")
                .font(.largeTitle)
                .foregroundColor(.primary)
            MultiDatePicker("Travel Dates", selection: $selectedDates)
                .frame(height: 300)
        }
        .padding()
    }
}
Display Selected Dates
We can display selected dates in a Text view as well. Let’s update our code to display selected dates.

struct ContentView: View {
    
    @State private var selectedDates: Set<DateComponents> = []
    @State private var formattedDates: String = ""
    
    let formatter = DateFormatter()
    
    var body: some View {
        VStack {
            Text("DevTechie!")
                .font(.largeTitle)
                .foregroundColor(.primary)
            MultiDatePicker("Travel Dates", selection: $selectedDates)
                .frame(height: 300)
                .onChange(of: selectedDates, perform: { _ in
                    formatSelectedDates()
                })
            ScrollView {
                LazyVStack(alignment: .leading) {
                    Text(formattedDates)
                }
            }
            .frame(maxWidth: .infinity)
        }
        .padding()
    }
    
    private func formatSelectedDates() {
        formatter.dateFormat = "MMM-dd-YY"
        let dates = selectedDates
            .compactMap { date in
                Calendar.current.date(from: date)
            }
            .map { date in
                formatter.string(from: date)
            }
        formattedDates = dates.joined(separator: "\n")
    }
}
Date Selection within Range
We can also limit the MultiDatePicker to specific ranges of dates allowing selections only before or after a certain date or between two dates.

Date selection before today

MultiDatePicker takes date range parameter, so if we want to limit date selection for dates before today, we can do something like this:

struct ContentView: View {
    
    @State private var selectedDates: Set<DateComponents> = []
    @State private var formattedDates: String = ""
    
    let formatter = DateFormatter()
    
    var body: some View {
        VStack {
            Text("DevTechie!")
                .font(.largeTitle)
                .foregroundColor(.primary)
            MultiDatePicker("Travel Dates", selection: $selectedDates, in: ..<Date())
                .frame(height: 300)
                .onChange(of: selectedDates, perform: { _ in
                    formatSelectedDates()
                })
            ScrollView {
                LazyVStack(alignment: .leading) {
                    Text(formattedDates)
                }
            }
            .frame(maxWidth: .infinity)
        }
        .padding()
    }
    
    private func formatSelectedDates() {
        formatter.dateFormat = "MMM-dd-YY"
        let dates = selectedDates
            .compactMap { date in
                Calendar.current.date(from: date)
            }
            .map { date in
                formatter.string(from: date)
            }
        formattedDates = dates.joined(separator: "\n")
    }
}
Date selection after today

In order to make dates selectable after today, we can create range like following:

struct ContentView: View {
    
    @State private var selectedDates: Set<DateComponents> = []
    @State private var formattedDates: String = ""
    
    let formatter = DateFormatter()
    
    var body: some View {
        VStack {
            Text("DevTechie!")
                .font(.largeTitle)
                .foregroundColor(.primary)
            MultiDatePicker("Travel Dates", selection: $selectedDates, in: Date()...)
                .frame(height: 300)
                .onChange(of: selectedDates, perform: { _ in
                    formatSelectedDates()
                })
            ScrollView {
                LazyVStack(alignment: .leading) {
                    Text(formattedDates)
                }
            }
            .frame(maxWidth: .infinity)
        }
        .padding()
    }
    
    private func formatSelectedDates() {
        formatter.dateFormat = "MMM-dd-YY"
        let dates = selectedDates
            .compactMap { date in
                Calendar.current.date(from: date)
            }
            .map { date in
                formatter.string(from: date)
            }
        formattedDates = dates.joined(separator: "\n")
    }
}
Date selection between two dates

To enable date selected between two dates, we will create from and to date range and pass it into the MultiDatePickerview, as shown below:

struct ContentView: View {
    
    @State private var selectedDates: Set<DateComponents> = []
    @State private var formattedDates: String = ""
    
    let formatter = DateFormatter()
    let fromDate = Calendar.current.date(from: DateComponents(year: 2022, month: 06, day: 15))!
    let toDate = Calendar.current.date(from: DateComponents(year: 2022, month: 06, day: 18))!
    
    var body: some View {
        VStack {
            Text("DevTechie!")
                .font(.largeTitle)
                .foregroundColor(.primary)
            MultiDatePicker("Travel Dates", selection: $selectedDates, in: fromDate..<toDate)
                .frame(height: 300)
                .onChange(of: selectedDates, perform: { _ in
                    formatSelectedDates()
                })
            ScrollView {
                LazyVStack(alignment: .leading) {
                    Text(formattedDates)
                }
            }
            .frame(maxWidth: .infinity)
        }
        .padding()
    }
    
    private func formatSelectedDates() {
        formatter.dateFormat = "MMM-dd-YY"
        let dates = selectedDates
            .compactMap { date in
                Calendar.current.date(from: date)
            }
            .map { date in
                formatter.string(from: date)
            }
        formattedDates = dates.joined(separator: "\n")
    }
}
Formatting MultiDatePicker
SwiftUI’s formatting modifiers works same as on any other view for MultiDatePicker. Let’s apply tint, padding and backgroundColor to our date picker.

struct ContentView: View {
    
    @State private var selectedDates: Set<DateComponents> = []
    @State private var formattedDates: String = ""
    
    let formatter = DateFormatter()
    let fromDate = Calendar.current.date(from: DateComponents(year: 2022, month: 06, day: 15))!
    let toDate = Calendar.current.date(from: DateComponents(year: 2022, month: 06, day: 18))!
    
    var body: some View {
        VStack {
            Text("DevTechie!")
                .font(.largeTitle)
                .foregroundColor(.primary)
            MultiDatePicker("Travel Dates", selection: $selectedDates, in: fromDate..<toDate)
                .frame(height: 300)
                .onChange(of: selectedDates, perform: { _ in
                    formatSelectedDates()
                })
                .tint(.orange)
                .padding()
                .background(Color.blue.opacity(0.2).gradient, in: RoundedRectangle(cornerRadius: 10))
            ScrollView {
                LazyVStack(alignment: .leading) {
                    Text(formattedDates)
                }
            }
            .frame(maxWidth: .infinity)
        }
        .padding()
    }
    
    private func formatSelectedDates() {
        formatter.dateFormat = "MMM-dd-YY"
        let dates = selectedDates
            .compactMap { date in
                Calendar.current.date(from: date)
            }
            .map { date in
                formatter.string(from: date)
            }
        formattedDates = dates.joined(separator: "\n")
    }
}


With that we have reached the end of this article. Thank you once again for reading. Subscribe to our weekly newsletter at https://www.devtechie.com