How to Use UICalendarView in iOS 16 | by Swapnanil Dhol | Jun, 2022

A UICalendarView Tutorial

UICalendarView

At WWDC ’22, Apple announced new controls to UIKit. In this tutorial, we will dive into one of the most useful and versatile component- UICalendarView.

The inline date-picker component of UIDatePicker is now available as a separate component.

A calendar view is used to show users specific dates along with additional information and decoration for those specific dates.

For example, in the Calendar app, the dates that have events are marked with a pinkish dot below the date. A calendar view can also be used to select one or multiple dates, or no dates at all.

You can pre-set dates, disable certain dates from being selected, and more with UICalendarView.

A UIDatePicker allows the user to select just one point in time. This selection is singular and cannot be used to select a range of, or multiple dates.

A date picker must still be used if you’d like to get input from the user about a specific point in time, such as the date for which a transaction has to be registered.

You use a calendar view only for the display and selection of dates. If you want to handle date and time selection, use UIDatePicker.

  • An important difference between UICalendarView and UIDatePicker is that UICalendarView represents dates as NSDateComponents in contrast to UIDatePicker which represents a particular point in time using NSDate.
  • Since UICalendarView‘s dates are represented by NSDateComponents, it is our responsibility to be explicit about the current calendar that we want to use. We cannot assume that, for example, the Gregorian calendar will be set by default. UICalendarView will configure itself with the system default calendar depending on the user’s current set calendar if a calendar is not passed explicitly.
Screenshot of Xcode showing the various available calendars

Here we can see a list of available calendars. We must be explicit in which calendar we’d like to use for our input.

To create a calendar view, we create an instance of UICalendarView and set its calendar. We can then add it to the view using auto-layout constraints.

let calendarView = UICalendarView()
calendarView.calendar = Calendar(identifier: .gregorian)
calendarView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(calendarView)
NSLayoutConstraint.activate([
calendarView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
calendarView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
calendarView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])

UICalendarView has a method to set the visible date range.

This range set can also be animated to show changes in response to a user input or a runtime conditional change.

To set visible date components, use this method

func setVisibleDateComponents(
_ dateComponents: DateComponents,
animated: Bool
)
// calendarView.setVisibileDateComponents(..., animated: true)

If the supplied date components are not in the same calendar as the UICalendarViewthe input date components will be converted to use UICalendarView.calendar upon assignment. This may result in date mismatch and might cause invalid dates if the two calendar systems don’t have the same dates.

We can set a selection behavior property on our calendar view to define what kind of input it will accept. We can configure it with single selection and multi selection. In this example, we are setting a multi-date selection.

let multiDateSelection = UICalendarSelectionMultiDate(delegate: self)

There is another selection method available called UICalendarSelectionSingleDate that is used to select single dates only.

The delegate of a UICalendarSelectionMultiDate must conform to UICalendarSelectionMultiDateDelegate. This delegate has 2 required methods and 2 optional methods.

  • The first method is didSelectDate and its function signature looks like this.
func multiDateSelection(_ selection: UICalendarSelectionMultiDate, didSelectDate dateComponents: DateComponents)

This method is called after the user selects a date in the calendar view.

  • The other required method is didDeselectDate and its function signature looks like this.
func multiDateSelection(_ selection: UICalendarSelectionMultiDate, didDeselectDate dateComponents: DateComponents)

This method is called after the user removes selection from one of the selected dates the calendar view.

Optional Methods

  • The first optionally available method is canSelectDate.
optional func multiDateSelection(_ selection: UICalendarSelectionMultiDate, canSelectDate dateComponents: DateComponents) -> Bool

This call determines if a date is selectable. Dates that are not selectable will be disabled in the calendar view.

  • The second optional delegate method is canDeselectDate.
optional func multiDateSelection(_ selection: UICalendarSelectionMultiDate, canDeselectDate dateComponents: DateComponents) -> Bool

This call determines if a date can be deselected.

UICalendarView lets us provide decorations for a date similar to what you might see in the system calendar app. This component has a wantsDateDecorations that is set to true by default.

However, you have to implement the delegate method to supply these decorations. This is what we will be looking at in this sub-section. Set calendarView‘s delegate to self and conform your view-controller to UICalendarViewDelegate.

calendarView.delegate = selfextension ViewController: UICalendarViewDelegate {}

This conformance requires us to implement one required method.

func calendarView(_ calendarView: UICalendarView, decorationFor dateComponents: DateComponents) -> UICalendarView.Decoration?

A decoration has 3 different types. Regular, Image, and Custom view.

Regular

The default init() creates a default decoration with a circle image as seen in the system calendar app.

With Image

This initialiser accepts an UIImagea UIColorand a UICalendarViewDecorationSize and creates a new image-based decoration with the specified image, color, and size.

  • The image defaults to circlebadge.filif no image is passed explicitly.
  • The color defaults to UIColor.systemFillColor if no color is passed explicitly.
  • The size defaults to UICalendarViewDecorationSizeMedium if no size is passed explicitly.

With Custom View

The Apple documentation explains this well.

Creates a new custom view decoration using the provided view provider. The provider will be called once when the decoration view is first loaded. The decoration will be clipped to its parent’s bounds, and cannot have interaction.

This initializer accepts a UIView and sets that as the calendar’s date’s decoration subject to the aforementioned restrictions.

UICalendarView.DecorationSize is an enum representing the available sizes for the decoration item.

UICalendarViewDecorationSizeSmall = 0,
UICalendarViewDecorationSizeMedium = 1,
UICalendarViewDecorationSizeLarge = 2,

To set a decoration view, simply provide the required decoration in the delegate method. For example. from the What’s New in UIKit session from WWDC ’22:

// Configuring Decorations
func calendarView(
_ calendarView: UICalendarView,
decorationFor dateComponents: DateComponents
) -> UICalendarView.Decoration? {
switch myDatabase.eventType(on: dateComponents) {
case .none:
return nil
case .busy:
return .default()
case .travel:
return .image(airplaneImage, color: .systemOrange)
case .party:
return .customView {
MyPartyEmojiLabel()
}
}
}

You can reload the decorations during runtime by calling the reloadDecorations(forDateComponents:animated:) method.

This accepts an array of DateComponentsdenoting the dates for which the decoration must be reloaded. You can even choose to animate these changes.

The new UICalendarView is a powerful component to select single and multiple dates as compared to a single point in time as provided by UIDatePicker. These two components have their own use-cases and shouldn’t be viewed as substitutes for either.

Use a UIDatePicker when you want to select a point in time. Use the new calendar view when you want to choose a single or a range of dates. A calendar view accepts DateComponentsvs a date picker that accepts a NSDate.

Thank you for reading!

Leave a Comment