In modern app development, creating reusable components is essential for building scalable and maintainable applications. In this blog post, we’ll walk through the process of creating a reusable SwiftUI view for selecting a date range based on a specified duration type (week, month, year) and incorporating buttons to navigate to the previous or next date range.

We’ll start by defining our reusable DateRangeSelectorView and then demonstrate how to integrate it into a parent view to show its usability. You can use this reusable component to any of your apps that allows filtering of data based on duration.

DateRangeSelectorView Implementation

Here’s the complete code for DateRangeSelectorView. This view allows users to select a date range and navigate between different ranges using “Previous” and “Next” buttons. When the view is loaded for the first time, default date range selected is current month start and end date.

import SwiftUI
import SwiftData

struct DateRangeSelectorView: View {

    @Binding var startDate: Date
    @Binding var endDate: Date
    @State var selectedDurationType: Constants.Duration = .month

    var dateRangeChanged: ((Date, Date) -> Void)?

    var body: some View {
        VStack {
            // Duration Picker
            Picker("DurationType", selection: $selectedDurationType) {
                Text("Week").tag(Constants.Duration.week)
                Text("Month").tag(Constants.Duration.month)
                Text("Year").tag(Constants.Duration.year)
            }
            .pickerStyle(.segmented)
            .frame(maxWidth: 150)
            .background(Color.appBackground)
            .cornerRadius(10)
            .onChange(of: selectedDurationType) {
                updateDateRangeAndNotify(direction: nil)
            }
            .onAppear {
                updateDateRangeAndNotify(direction: nil)
            }

            // Date Range Display
            HStack {
                // Previous Button
                ZStack {
                    Circle()
                        .foregroundColor(.appBackground)
                    Button(action: {
                        updateDateRangeAndNotify(direction: Constants.Direction.backward)
                    }) {
                        Image(systemName: "arrow.left")
                    }
                    .foregroundColor(.white)
                }
                .frame(width: 30)

                // Start and End Dates
                Text("\\\\(startDate.formatted(date: .abbreviated, time: .omitted))")
                Text(" - ")
                Text("\\\\(endDate.formatted(date: .abbreviated, time: .omitted))")

                // Next Button
                ZStack {
                    Circle()
                        .foregroundColor(.appBackground)
                    Button(action: {
                        updateDateRangeAndNotify(direction: Constants.Direction.forward)
                    }) {
                        Image(systemName: "arrow.right")
                    }
                    .foregroundColor(.white)
                }
                .frame(width: 30)
            }
            .font(.system(size: 15))
        }
        .frame(alignment: .center)
        .cornerRadius(10)
    }

    private func updateDateRangeAndNotify(direction: Constants.Direction?) {
        if direction == .forward {
            let nextDateRange = AppUtil.getNextDateRange(forDuration: selectedDurationType, forDirection: .forward, usingStartDate: startDate, usingEndDate: endDate)
            startDate = nextDateRange.nextStartDate
            endDate = nextDateRange.nextEndDate
        } else if direction == .backward {
            let previousDateRange = AppUtil.getNextDateRange(forDuration: selectedDurationType, forDirection: .backward, usingStartDate: startDate, usingEndDate: endDate)
            startDate = previousDateRange.nextStartDate
            endDate = previousDateRange.nextEndDate
        } else {
            let currentDuration = AppUtil.getCurrentDurationStartAndEndDates(forDuration: selectedDurationType)
            startDate = currentDuration.startDate
            endDate = currentDuration.endDate
        }
        dateRangeChanged?(startDate, endDate)
    }
}

#Preview {
    DateRangeSelectorView(
        startDate: .constant(Date()),
        endDate: .constant(Calendar.current.date(byAdding: .day, value: 6, to: Date()) ?? Date())
    ) { newFromDate, newToDate in
        print("Date range changed: From \\\\(newFromDate) to \\\\(newToDate)")
    }
}


Integrating DateRangeSelectorView into a Parent View

To demonstrate how to use DateRangeSelectorView, we’ll integrate it into a parent view. This example shows how to manage the selected date range and respond to changes.

import SwiftUI

struct ContentView: View {

    @State private var startDate: Date = Date()
    @State private var endDate: Date = Calendar.current.date(byAdding: .day, value: 6, to: Date()) ?? Date()

    var body: some View {
        VStack {
            Text("Select Date Range")
                .font(.title)
                .padding()

            DateRangeSelectorView(startDate: $startDate, endDate: $endDate) { newFromDate, newToDate in
                print("Date range updated: From \\\\(newFromDate) to \\\\(newToDate)")
            }
            .padding()

            Spacer()
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


Explanation

  1. Picker for Duration Type: The picker allows users to select between week, month, and year durations. When the selection changes, the date range is updated accordingly. Selection of duration type recalculates dates based on current date and duration time selected.
  2. Date Range Display: This section displays the current start and end dates and provides “Previous” and “Next” buttons to navigate through date ranges.
  3. Updating Date Ranges: The updateDateRangeAndNotify function handles updating the date range based on the selected duration and the navigation direction (previous or next). It uses helper services ( AppUtil) to compute the new date ranges.
  4. Integration in Parent View: ContentView integrates DateRangeSelectorView and manages the state for startDate and endDate. It also provides a callback to handle date range changes so that the parent view can update its contents based on new date range.

Conclusion

By creating a reusable DateRangeSelectorView, you can easily integrate date range selection functionality into various parts of your SwiftUI applications. This approach not only promotes code reuse but also simplifies the management of date ranges within your app. Happy coding!

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.