edonv / CalendarView

SwiftUI wrapper for UICalendarView.
https://swiftpackageindex.com/edonv/CalendarView/documentation/calendarview
MIT License
2 stars 1 forks source link
calendarview ios swift swiftui uikit

CalendarView

CalendarView is a SwiftUI wrapper of UICalendarView, which is a view that displays a calendar that provides for user selection of a single date or multiple dates. It also allows for displaying date-specific decorations.

More info on its full capabilities can be found at Apple's documentation for UICalendarView.

Basic Usage

The minimum needed to work with CalendarView is to attach it to a Binding variable with a DateComponents type. If you want to limit date selection to a single date at a time, use an optional DateComponents. To allow multiple date selection, the Binding should be a Set<DateComponents> (a Set of DateComponents).

[!TIP] If you don't need to track any selection, you can also omit this parameter entirely.

And, alternatively, if using multiple selection and the order the selections are made matters, you can use [DateComponents] instead of a Set.

struct ContentView: View {
    @State
    private var singleDateSelection: DateComponents? = nil

    @State
    private var multipleDateSelection: Set<DateComponents> = []

    var body: some View {
        VStack {
            // No selection tracking
            CalendarView()

            // Single date selection tracking
            CalendarView($singleDateSelection)

            // Multiple date selection tracking
            CalendarView($multipleDateSelection)
        }
    }
}

[!WARNING] When updating the selection Binding outside of CalendarView, be wary.

If you manually create or make changes to a DateComponents instance output from CalendarView with components that aren't exactly the same as those set internally by the view, you might get unexpected behavior, such as duplicate selections and more.

It's recommended to use the Binding as a "read-only" property and not to write to it directly.

Advanced Configuration

Environment Values

Some elements of the CalendarView can be set with SwiftUI Environment and its standard ViewModifier (environment(_:_:)).

This also means that CalendarView also reacts to these settings if they're set in the parent Environment. And for convenience, the package comes with abbreviated modifiers to directly set these values.

These elements are:

CalendarView($selectedDate)
    // Environment Modifier
    // Calendar
    .environment(\.calendar, Calendar(identifier: .chinese))
    // Locale
    .environment(\.locale, Locale(identifier: "en_GB"))
    // Time Zone
    .environment(\.timeZone, TimeZone(identifier: "America/Chicago")!)

    // Convenience Modifiers
    // Calendar
    .calendar(Calendar(identifier: .chinese))
    // Locale
    .locale(Locale(identifier: "en_GB"))
    // Time Zone
    .timeZone(TimeZone(identifier: "America/Chicago")!)

More Configuration

Decorations

Another feature of UICalendarView (and therefore, CalendarView by proxy), is to display "decorations" along with any of the dates in the calendar. These can be different for different dates in the calendar, one type of decoration for all dates, or no decorations whatsoever.

The different types of decorations are as follows:

[!TIP] You can use multiple decoration modifiers to specify different decorations for different dates (or for all dates not explicitly specified in other modifiers).

A decoration for a explicitly specified for a particular date takes precedence over a decoration specified for no specific date.

CalendarView($selectedDate)
    // This sets an orange `star.fill` symbol under all dates in the calendar.
    .decorations(.image("star.fill", color: .orange))
    // This displays the month number under each date (why not?) using a SwiftUI ViewBuilder
    .decorations { dateComponents in
        Text(dateComponents.month ?? 0, format: .number)
    }

Font Design

The font design that the calendar view uses for displaying calendar text. CalendarView comes with a ViewModifier to set this:

CalendarView($selectedDate)
    .fontDesign(.rounded)

Visible Date Components

Using the visibleDateComponents parameter in CalendarView's initializer (Binding<DateComponents?>), you can control the visible part of in the calendar view. It can also be used to keep track of what part of the calendar is currently be used.

struct ContentView: View {
    @State
    private var selectedDate: DateComponents? = nil

    // This sets the initial month shown to May 2024
    @State
    private var currentlyViewedComponents: DateComponents? = .init(
        calendar: calendar,
        year: 2024,
        month: 5
    )

    var body: some View {
        CalendarView(
            $selectedDate,
            visibleDateComponents: $currentlyViewedComponents
        )
    }
}

[!IMPORTANT] If visibleDateComponents's calendar property is set to a different Calendar than the view's calendar (see Environment Values), the view uses the current Environment's calendar, which may result in an invalid date from the date components.

Available Date Range

Using the availableDateRange parameter in CalendarView's initializer (DateInterval), you can control the range of dates that the calendar view is able to display. Setting this parameter restricts the earliest or latest dates that the calendar view displays. The default date range is from distantPast to distantFuture.

For convenience, an extra protocol comes with the package: DateRangeExpression, which allows a DateInterval to be initialized from a few of the range types. Additionally, it comes with support for using "Range-literal" syntax with Date values to create a DateInterval value. Specifically, it works with the syntax for:

CalendarView(
    $selectedDate,
    // This would only allow the user to select
    // a date from the current moment in time onward.
    availableDateRange: Date.now...
)