The elegant full screen calendar missed in SwiftUI.

Overview

ElegantCalendar

Platforms License: MIT

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


ElegantTimeline - Shows what's possible using ElegantCalendar

Comes with 8 default themes. You can also configure your own theme. Read more to find out.

Introduction

ElegantCalendar is inspired by TimePage and is part of a larger repository of elegant demonstrations like this: TimePage Clone. It 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(like ElegantTimeline), not as a full screen date picker(the demo demonstrates how to do so if you really want to).

Features:

  • Display months and years in a full screen vertical scrolling layout
  • Custom layout system that allows virtually infinite date ranges with minimal increasing memory usage
  • Customization of individual day views
  • Customization of the calendar color scheme, light and dark
  • Customization of the accessory view displayed when selecting a day
  • Excluding certain days from being selectable on the calendar
  • Scrolling to a particular day, month, or year with or without animation
  • Built in button that scrolls back to today’s month or year
  • Flexibility in either using the full calendar view that has both the monthly and yearly view or just one of the individual views
  • Haptics when performing certain actions
  • Intuitive navigation between the yearly and monthly view: swipe between views or tap on the month header to navigate to the yearly view
  • Elegant default themes

Basic usage

Using ElegantCalendar is as easy as:

import ElegantCalendar

struct ExampleCalendarView: View {

    // Start & End date should be configured based on your needs.
    let startDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (-30 * 36)))
    let endDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (30 * 36)))

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

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

}

However, if you just want an individual view, not the entire calendar view, you can do either:

import ElegantCalendar

struct ExampleMonthlyCalendarView: View {

    // Start & End date should be configured based on your needs.
    let startDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (-30 * 36)))
    let endDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (30 * 36)))

    @ObservedObject var calendarManager = MonthlyCalendarManager(
        configuration: CalendarConfiguration(startDate: startDate,
                                             endDate: endDate))

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

}

struct ExampleYearlyCalendarView: View {

    // Start & End date should be configured based on your needs. 
    let startDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (-30 * 36)))
    let endDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (30 * 36)))

    @ObservedObject var calendarManager = YearlyCalendarManager(
        configuration: CalendarConfiguration(startDate: startDate,
                                             endDate: endDate))

    var body: some View {
        YearlyCalendarView(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

ElegantCalendarManager

configuration: The configuration of the calendar view

public struct CalendarConfiguration: Equatable {

    let calendar: Calendar
    let ascending: Bool // reverses the order in which the calendar is laid out
    let startDate: Date
    let endDate: Date

}

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: MonthlyCalendarDataSource, YearlyCalendarDataSource { }

public protocol MonthlyCalendarDataSource {

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

}

public protocol YearlyCalendarDataSource { }

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: MonthlyCalendarDelegate, YearlyCalendarDelegate { }

public protocol MonthlyCalendarDelegate {

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

}

public protocol YearlyCalendarDelegate {

    func calendar(didSelectMonth date: Date)
    func calendar(willDisplayYear 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.

theme: The theme of various components of the calendar. Default is royal blue. Available for ElegantCalendarView & YearlyCalendarView & MonthlyCalendarView.

public struct CalendarTheme: Equatable, Hashable {

    let primary: Color

}

public extension CalendarTheme {

    static let brilliantViolet = CalendarTheme(primary: .brilliantViolet)
    static let craftBrown = CalendarTheme(primary: .craftBrown)
    static let fluorescentPink = CalendarTheme(primary: .fluorescentPink)
    static let kiwiGreen = CalendarTheme(primary: .kiwiGreen)
    static let mauvePurple = CalendarTheme(primary: .mauvePurple)
    static let orangeYellow = CalendarTheme(primary: .orangeYellow)
    static let red = CalendarTheme(primary: .red)
    static let royalBlue = CalendarTheme(primary: .royalBlue)

}

ElegantCalendarView(...)
    .theme(.mauvePurple)

To configure your own theme, just pass in your color into the CalendarTheme initializer. To have dynamic appearance, make sure your Color has both a light and dark appearance.

horizontal or vertical: The orientation of the calendar. The default is horizontal, as shown in the GIF. Available for ElegantCalendarView & YearlyCalendarView & MonthlyCalendarView.

ElegantCalendarView(...)
    .vertical()

allowsHaptics: Whether haptics is enabled or not. Default is enabled. Available for ElegantCalendarView & MonthlyCalendarView

ElegantCalendarView(...)
    .allowsHaptics(false)

Users get haptics whenever they tap a day, scroll to a new month, or press the scroll back to today button.

frame: Custom width for the monthly calendar view. Available for MonthlyCalendarView

MonthlyCalendarView(...)
    .frame(width: ...)

Use Cases

The following aspects of ElegantCalendarManager can be used:

var currentMonth: Date - The current month displayed on the calendar view.

var selectedDate: Date? - The date selected on the calendar view, if any.

var isShowingYearView: Bool - Whether the year view is showing. If false, the month view is showing.

func scrollToMonth(_ month: Date, animated: Bool = true) - Scroll back to a certain month, animated or not. No date is selected in the process.

func scrollBackToToday(animated: Bool = true) - Scroll back to today, animated or not. Today's date is selected in the process.

func scrollToDay(_ day: Date, animated: Bool = true) - Scroll back to a certain date, animated or not. The date is selected in the process.

Demos

The demos 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.

let package = Package(
  name: "TestProject",
  dependencies: [
    .package(url: "https://github.com/ThasianX/ElegantCalendar", from: "4.2.0")
  ],
  targets: [
    .target(name: "TestProject", dependencies: ["ElegantCalendar"])
  ]
)

Inside whatever app is using ElegantCalendar or your Swift Package that uses ElegantCalendar as a dependency:

  1. Scroll the project navigator down to the Swift Package Dependencies section. Inside ElegantCalendar, you'll see a directory called ElegantCalendar.xcassets.
  2. After you've located it, open your project's settings and navigate to your target's build phases in a parallel window.
  3. Drag ElegantCalendar.xcassets into your target's Copy Bundle Resources. Make sure that Copy items if needed is unticked and Create groups is ticked. This step is crucial because ElegantCalendar uses custom icons, which SPM will support in Swift 5.3.
  4. This last step is for making sure that when others clone your repository, the assets will be available to them as well. Click the ElegantCalendar.xcassets that has appeared in your project navigator and in the inspector on the right, select Identity and Type. Inside, make sure that Location is set to Relative to Build Products.

If you don't know how to do this, refer to the Demo.

Requirements

  • iOS 13.0+
  • Xcode 11.0+

Contributing

If you find a bug, or would like to suggest a new feature or enhancement, it'd be nice if you could search the issue tracker first; while we don't mind duplicates, keeping issues unique helps us save time and considates effort. If you can't find your issue, feel free to file a new one.

Resources

Also, here's a dump of resources I found useful when working on this

License

This project is licensed under the MIT License - see the LICENSE file for details

Comments
  • Full screen calendar with horizontal scroll on months.

    Full screen calendar with horizontal scroll on months.

    Hi, I am evaluating multiple SwiftUI Customizable calendar libraries to use with my new app. I would want to achieve something like this image below. Would it be possible to achieve this using ElegantCalendar? If yes, what's the path should I be taking to do this? Happy to contribute as well if something is missing.

    Thanks in advance.

    IMG_4D182DB6382F-1

    opened by sethi-ishmeet 16
  • Missing Calendar

    Missing Calendar

    Have you seen any issues when using 2 MonthlyCalendars in a single app? I am using one instance for schedule data and a separate instance with its own data and behavior for event data. When I do this both calendars disappear. As soon as I comment out one of the calendars, then the other works properly. Screen Shot 2020-08-22 at 6 20 39 PM

    opened by frios 5
  • iOS 15 Warnings in Xcode 13 when using ElegantCalendar

    iOS 15 Warnings in Xcode 13 when using ElegantCalendar

    Issue When running the Example project with Xcode 13, I am getting warnings in the console log:

    ForEach<Array<String>, String, ModifiedContent<ModifiedContent<Text, _FrameLayout>, _EnvironmentKeyWritingModifier<Optional<Color>>>>: the ID T occurs multiple times within the collection, this will give undefined results!

    There are six of these in a series logged the calendar is displayed.

    iOS Version This only happens when running against iOS 15. When I run against a simulator in iOS 14, it is quiet and I only get the expected messages from the Example project.

    (This too is happening from my app that uses this package, but was easier and cleaner to verify this behavior in the include Example here).

    I will try to hunt this down, but maybe someone else already has a solution. The calendar does appear to be working iOS 15 despite these warnings, but worry something in SwitUI may break the cause of this in the future.

    opened by Danny-SS 4
  • .frame() modifier shifts the calendar to the right if used with .horozontal() scrolling

    .frame() modifier shifts the calendar to the right if used with .horozontal() scrolling

    Hi,

    I saw the horizontal monthly calendar scrolling was implemented recently which is cool. Although when you apply .frame() modifier to MonthlyCalendarView it is being shifted to the right. Please see the screenshot. I guess it is related to ElegantPages? Could you point me in the right direction and I would try to find the issue.

    I am new to coding and it's the first time I submit an issue, please tell me if I did something wrong. It's a very cool calendar library, trying to implement it in the habit tracker app I am working on.

    Screenshot 2020-08-06 at 21 53 32
    opened by maray29 4
  • Days of the Week appear correctly on Simulator but not on device.

    Days of the Week appear correctly on Simulator but not on device.

    Great job on developing this project. The calendar looks wonderful!

    I am experiencing a strange issue with the example project where the start of the month doesn't match up with the actual days of the week in the Monthly Calendar view. Here is the screenshot from my simulator: Simulator Screen Shot - iPod touch (7th generation) - 2021-03-03 at 15 45 48

    As you can see, today is Wednesday 3 March 2021 and the calendar view is aligned correctly with the days of the week.

    However, when I run the example project on my iPhoneX it shows this: IMG_2B5E38DCFEF7-1

    As you can see, it aligns today's date(Wednesday 03 March 2021) with Tuesday rather than Wednesday.

    I haven't changed any code yet the CalendarView displays differently on Simulator and device. What could be causing this?

    opened by yomagicdev 3
  • how to add color coded for specific days ?

    how to add color coded for specific days ?

    I want to add a color code on important dates, both in month and whole year view. is it possible?

    is it possible to have an array of dates to be colored on the calender differently? pls add if cant ty.

    opened by Darren120 3
  • Any way to move the calendar up the screen?

    Any way to move the calendar up the screen?

    Body of view:

    var body: some View { MonthlyCalendarView(calendarManager: calendarManager) .theme(CalendarTheme(primary: Color.mFoodColor)) .allowsHaptics(true) .horizontal() // .offset(x: 0.0, y: -50.0) .navigationTitle("Schedule") }

    Screen Shot 2020-08-20 at 5 16 43 PM

    There is a big whitespace above the Month name. I have tried adding Spacer() in various places. The only way I have been able to change this is to use the .offset that is commented out in the above code.

    Are there better ways to correct this?

    opened by frios 3
  • Navigation issue

    Navigation issue

    Thank you for the beautiful library. When you open the library for navigation, it shows the month, and when you press back, it returns to the year display and then goes to the other screen.

    opened by metin-atalay 2
  • Segue from calendar date to another page

    Segue from calendar date to another page

    I can not figure out how to segue from the visit list cell to another view controller when I add a tap gesture recognizer to this part of the code. I would like to be able to go to another page on my app once the user selects a certain appointment on a date. Thank you

    var visitsPreviewList: some View {

        VStack(spacing: 0) {
            ForEach(visits[range]) { visit in
                VisitCell(visit: visit)
                    .gesture(TapGesture().onEnded {
                        //Would like to segue to another ViewController here
                        print(visit.uid)
                    })
            }
        }
    }
    
    opened by samuelbv9 2
  • Error: `ElegantCalendar.xcassets` doesn't exist

    Error: `ElegantCalendar.xcassets` doesn't exist

    I imported library by using Swift packages. After add ElegantCalendarView to view's body, I received fatalError at Image+Custom.swift file at line 9. I found that "uturn.left" image is already existed in ElegantCalendar.xcassets. please tell me how can I fix this?

    opened by hungbv-0181 2
  • Dynamic 'startDate' and 'endDate'

    Dynamic 'startDate' and 'endDate'

    Is there any way to change startDate and endDate dynamically?

    I was looking into the source code and found a line in CalendarConfiguration.swift: "/// Any changes to the configuration will reset the calendar based on its new settings"

    But I didn't understand how to do it. First, the CalendarConfiguration have let property itself in MontlyCalendarManager and in the CalendarConfiguration struct the startDate and endDate properties are also have let

    When I change them to var, the dates changes, but the months remain the same. In which direction should I move?

    opened by thrustToga 2
Releases(4.3.2)
  • 4.3.2(Sep 22, 2021)

  • 4.3.0(Aug 4, 2020)

  • 4.2.0(Aug 1, 2020)

    • Updated ElegantPages to fix bug where the delegate was being called at calendar creation
    • Fixed bug where if the end date was the exact start of a given month, that month wouldn’t be shown
    • Lowered the swift tools version. Now compatible with any Xcode 11 version!
    • Updated the examples
    Source code(tar.gz)
    Source code(zip)
  • 4.1.0(Jul 17, 2020)

  • 4.0.1(Jul 15, 2020)

  • 4.0.0(Jul 15, 2020)

    8 new themes that look elegant in any app!

    Major changes:

    CalendarConfiguration now doesn't manage the calendar's theme or haptics settings anymore. This makes sense because a change in the configuration should result in a complete reset of the calendar. This means that whatever month or year the user is scrolled on currently is reset as well. In this line of thought, it doesn't make sense for a full reset to occur whenever the theme or haptic settings change. These changes should only result in a few minor adjustments in appearances and logic. State is preserved and user experience remains well now whenever the theme or haptics settings are changed now.

    The new syntax for this is:

    ElegantCalendarView(...).theme(_yourTheme or one of the default ones) ElegantCalendarView(...).allowsHaptics(_whether or not you want to enable haptics)

    See README.md customization for more info. The great thing about this is that you can now change the theme or haptics settings by passing in a binding variable, which is a great trend towards declarative code.

    Source code(tar.gz)
    Source code(zip)
  • 3.0.0(Jul 14, 2020)

    Calendar scrolls are quicker now in line with the new haptics and just feels better in general. The monthly header and day view text font size has been decreased just a tad bit in proportion to the view size. The scroll back to today icon has been replaced with a custom icon that looks much cleaner.

    Major change: Use of custom icons instead of SF Symbol icons. See README.md instructions on how to use the new icons.

    Source code(tar.gz)
    Source code(zip)
  • 2.0.0(Jul 13, 2020)

    If you want, you can now just use the yearly or monthly view in your app. The models for these views have been made public and refined for greater flexibility. To use these views by themselves, use the correct ObservableObject: MonthlyCalendarManager or YearlyCalendarManager.

    Major change: The naming of a delegate function has also been changed: func calendar(didSelectDate date: Date) -> func calendar(didSelectDay date: Date).

    Source code(tar.gz)
    Source code(zip)
  • 1.3.0(Jul 10, 2020)

    The haptics are enabled by default in the configuration so if you don't want haptics, you will have to specify that in your configuration creation. Also, for public functions like scrollToMonth or scrollToDay, you may also choose whether to use haptics or not through the animated parameter.

    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(Jul 3, 2020)

Owner
Kevin Li
Sophomore at OSU. SWE Intern @ Tesla
Kevin Li
RCalendarPicker A date picker control, Calendar calendar control, select control, calendar, date selection, the clock selection control.

RCalendarPicker RCalendarPicker Calendar calendar control, select control, calendar, date selection, the clock selection control. 日历控件 ,日历选择控件,日历,日期选择

杜耀辉 131 Jul 18, 2022
A declarative, performant, iOS calendar UI component that supports use cases ranging from simple date pickers all the way up to fully-featured calendar apps.

HorizonCalendar A declarative, performant, calendar UI component that supports use cases ranging from simple date pickers all the way up to fully-feat

Airbnb 2.2k Jan 4, 2023
Simple customizable calendar component in Swift :calendar:

Koyomi Koyomi is a simple calendar view framework for iOS, written in Swift ?? Content Features Demo App Usage introduction : Change displayed month,

Shohei Yokoyama 741 Dec 24, 2022
Malendar is a personal calendar app that connects to your default calendar and lets you add/delete events

Malendar is a personal calendar app that connects to your default calendar and lets you add/delete events. It will gather events from your default iOS calendar.

Chase 194 Jan 4, 2023
Full featured lunar calendar library

LunarCore 小历(iOS & OS X)的农历核心部分。 何为小历 小历是一个简洁的农历 app,目前支持 iOS & OS X 两端,iOS 端多次被 App Store 官方推荐。 前世今生 LunarCore 最早来自于一个 JavaScript 编写的农历库:LunarCalenda

Ying Zhong (Inactive) 782 Aug 31, 2022
📆 An elegant calendar control for iOS.

NO LONGER MAINTAINED Daysquare An elegant calendar control for iOS. Introduction Get bored with native silly UIDatePicker? You may have a try on this

Cyandev 701 Sep 9, 2022
SwiftUI Simple Calendar / Date Picker for iOS

RKCalendar RKCalendar is a SwiftUI Calendar / Date Picker for iOS. Features include: minimum and maximum calendar dates selectable, single date select

null 453 Dec 28, 2022
A customizable swiftui calendar

Description not available. Installation From Xcode 11, you can use Swift Package Manager to add Kingfisher to your project. Select File > Swift Packag

Heshan Yodagama 140 Dec 24, 2022
An easy to use SwiftUI date picker for Shamsi (Persian) calendar

ShamsiDatePicker An easy-to-use SwiftUI iOS/watchOS date picker for Shamsi (Persian) calendar. Features Pure (100%) SwiftUI implementation Full suppor

Seyyed Parsa Neshaei 20 Nov 24, 2022
SwiftUICalendar - SwiftUI simple calendar

SwiftUICalendar Installation CocoaPods pod 'SwiftUICalendar' import import SwiftUICalendar Features Infinite scroll Support horizontal and vertical sc

null 59 Dec 27, 2022
Clendar - universal calendar app. Written in SwiftUI. Available on App Store. MIT License.

Clendar - minimal calendar Minimal Calendar & Widgets Landing Page About This project is started out as an UIKit base app for me to learn new WWDC fea

Vinh Nguyen 418 Dec 30, 2022
A SwiftUI calendar view that allows month switching and date picking.

Selectable Calendar View A SwiftUI calendar view that allows month switching and date picking. Usage You can simply add this repository to your projec

シュンジョーァ 10 Jul 21, 2022
📅 Calendar for iOS, iPadOS and macOS in Swift

CalendarKit CalendarKit is a Swift calendar UI library for iOS, iPadOS and Mac Catalyst. It looks similar to the Apple Calendar app out-of-the-box, wh

Richard Topchii 2.2k Jan 5, 2023
An Easy to Use Calendar for iOS (Swift 5.0)

This is an easy to use, "just drag and drop it in your code" type of calendar for iOS. It supports both vertical and horizontal scrolling, as well as

Michael Michailidis 525 Dec 23, 2022
Dead simple calendar implementation

Package provides a CalendarView which can be used to display simple calendar in your App.

Sergei Kononov 4 Oct 7, 2022
A calendar quick view for the MacOS status bar

Calendar Quick View Quick Menu Calendar in the mac app store An open source macOS calendar preview utility Download from the Mac App Store Visualizati

Michaellis 18 Oct 26, 2022
A custom visual calendar for iOS 8+ written in Swift (>= 4.0).

Overview Screenshots GIF Demo Installation Usage Architecture Version matrix Advanced API For contributors Screenshots GIF Demo Installation CocoaPods

null 3.5k Dec 24, 2022
iOS 7+ Calendar (Date Picker) with Infinite Scrolling.

RSDayFlow iOS 7 Calendar with Infinite Scrolling. Only need 4 lines of code to set up. RSDayFlow is a slim fork of DayFlow with updates and extensions

Ruslan Skorb 844 Sep 14, 2022
An availability calendar implementation for iOS

NWCalendarView NWCalendar View is an iOS control that displays a calendar. It is perfect for appointment or availibilty selection. It allows for selec

Nicholas Wargnier 60 May 27, 2021