ElegantCalendar

ElegantCalendar is an efficient and customizable full screen calendar written in SwiftUI.

ElegantCalendar is inspired by TimePage. It also uses ElegantPages, another library I wrote specifically for paging so check that out :)

It is mainly meant to be used with apps that require the use of a calendar to function, not as a full screen date picker(may be supported in future).

Also, sadly I upgraded my iPhone to iOS14 the other day and couldn't run the app on my phone :c. So could only get screenshots for light mode :c

Basic usage

Using ElegantCalendar is as easy as:


import ElegantCalendar

struct ExampleCalendarView: View {

    @ObservedObject var calendarManager = ElegantCalendarManager(
        configuration: CalendarConfiguration(startDate: startDate,
                                             endDate: endDate,
                                             themeColor: .blackPearl))

    var body: some View {
        ElegantCalendarView(calendarManager: calendarManager)
    }

}

How it works

ElegantCalendarView uses the ElegantHPages view from ElegantPages. Essentially, it's just a swipable HStack that loads all the views immediately. And it's also for this reason that it is not recommended that ElegantCalendarView should not be used as a date picker. Here's why.

Let's first talk about the monthly calendar where you can swipe up and down to see the next/previous month. This view uses ElegantVList and is really efficient memory and performance wise. When it comes to the yearly calendar, performance is just as amazing. However, the catch is that all the year views have to be loaded into memory and drawn onto the screen first. This takes a few seconds depending on your date range, the wider the longer. However, once this loading process is over, the calendar functions smoothly and elegantly.

So how can this be fixed? Either create a simpler yearly calendar that doesn't require as much CoreGraphics drawing as the current one or load the year views on demand. The problem with the second approach is that SwiftUI is just inefficient at making views, as it spends a LOT of CPU on rendering. Hopefully, in future iterations of SwiftUI, the rendering becomes smoother. As for the former approach, it seems the most feasible and I will consider implementing it if enough people display interest. Just make an issue about it so I can tell.

Customization

The following aspects of ElegantCalendarView can be customized:

configuration: The configuration of the calendar view


public struct CalendarConfiguration: Equatable {

    let calendar: Calendar
    let startDate: Date
    let endDate: Date
    let themeColor: Color

}

initialMonth: The initial month to display on the calendar. If not specified, automatically defaults to the first month.

datasource: The datasource of the calendar


public protocol ElegantCalendarDataSource {

    func calendar(backgroundColorOpacityForDate date: Date) -> Double
    func calendar(canSelectDate date: Date) -> Bool
    func calendar(viewForSelectedDate date: Date, dimensions size: CGSize) -> AnyView

}

This allows you to customize the opacity of any given day, whether you want a day to be tappable or not, and the accessory view that shows when a day is tapped.

delegate: The delegate of the calendar


public protocol ElegantCalendarDelegate {

    func calendar(didSelectDate date: Date)
    func calendar(willDisplayMonth date: Date)

}

This is just a convenience to handle the shortcomings of the @Published wrapper which doesn't support didSet. Conform to this if you need to do things when a month is displayed or date changes.

Demos

The demo shown in the GIF can be checked out on example repo.

Installation

ElegantCalendar is available using the Swift Package Manager:

Using Xcode 11, go to File -> Swift Packages -> Add Package Dependency and enter https://github.com/ThasianX/ElegantCalendar

If you are using Package.swift, you can also add ElegantCalendar as a dependency easily.


dependencies: [
    ...
    .package(url: "https://github.com/ThasianX/ElegantCalendar", .upToNextMajor(from: "1.0.0"))
    ...
]

Requirements

  • iOS 13.0+
  • Xcode 11.0+

GitHub