- Published on
UICalendarView Tutorial
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
.
What is 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
.
How is it different from a UIDatePicker?
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
.
Considerations
- An important difference between
UICalendarView
andUIDatePicker
is thatUICalendarView
represents dates asNSDateComponents
in contrast toUIDatePicker
which represents a particular point in time usingNSDate
. - Since
UICalendarView
’s dates are represented byNSDateComponents
, 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.
Here we can see a list of available calendars. We must be explicit in which calendar we’d like to use for our input.
Getting started
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)
])
Setting date range
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
UICalendarView
, the input date components will be converted to useUICalendarView.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.
Handling Date Selections
We can set a selection behaviour 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.
Required 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.
Customisation
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 = self
extension ViewController: UICalendarViewDelegate {}
This conformance requires us to implement one required method.
func calendarView(_ calendarView: UICalendarView, decorationFor dateComponents: DateComponents) -> UICalendarView.Decoration?
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 UIImage
, a UIColor
, and a UICalendarViewDecorationSize
and creates a new image-based decoration with the specified image, color, and size.
- The
image
defaults tocirclebadge.fill
if no image is passed explicitly. - The
color
defaults toUIColor.systemFillColor
if no color is passed explicitly. - The
size
defaults toUICalendarViewDecorationSizeMedium
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 initialiser 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 this method.
reloadDecorations(forDateComponents:animated:)
This method accepts an array of DateComponents
denoting the dates for which the decoration must be reloaded. You can even choose to animate these changes.
Conclusion
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 of 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 DateComponents
vs a date picker that accepts a NSDate
.
Thank you for reading!