{"id":2530,"url":"https://github.com/ThasianX/ElegantCalendar","last_synced_at":"2025-08-06T16:30:53.102Z","repository":{"id":40612791,"uuid":"270397113","full_name":"ThasianX/ElegantCalendar","owner":"ThasianX","description":"The elegant full screen calendar missed in SwiftUI.","archived":false,"fork":false,"pushed_at":"2023-12-24T19:20:38.000Z","size":31293,"stargazers_count":895,"open_issues_count":10,"forks_count":94,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-12-08T19:08:48.704Z","etag":null,"topics":["calendar","elegantcalendar","ios-animation","swift-package-manager","swiftui","swiftui-animations","swiftui-components","swiftui-example"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ThasianX.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-06-07T18:34:43.000Z","updated_at":"2024-11-29T05:35:16.000Z","dependencies_parsed_at":"2024-01-05T20:19:52.850Z","dependency_job_id":"1ec32b01-566d-496e-9130-9ba74b3d91c6","html_url":"https://github.com/ThasianX/ElegantCalendar","commit_stats":{"total_commits":131,"total_committers":5,"mean_commits":26.2,"dds":0.03816793893129766,"last_synced_commit":"3d1e8369ffb233e392ee36ad6ff0fd38ad4846b2"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ThasianX%2FElegantCalendar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ThasianX%2FElegantCalendar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ThasianX%2FElegantCalendar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ThasianX%2FElegantCalendar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ThasianX","download_url":"https://codeload.github.com/ThasianX/ElegantCalendar/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228923688,"owners_count":17992566,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["calendar","elegantcalendar","ios-animation","swift-package-manager","swiftui","swiftui-animations","swiftui-components","swiftui-example"],"created_at":"2024-01-05T20:16:16.054Z","updated_at":"2024-12-09T16:31:01.215Z","avatar_url":"https://github.com/ThasianX.png","language":"Swift","funding_links":[],"categories":["UI","Libs","Calendar","iOS","Swift","UI [🔝](#readme)"],"sub_categories":["Calendar","UI","Content","Android samples"],"readme":"# ElegantCalendar\n\n\u003cp align=\"leading\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/platform-iOS-blue.svg?style=flat\" alt=\"Platforms\" /\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Swift-5-orange.svg\" /\u003e\n    \u003ca href=\"https://github.com/ThasianX/Elegant-Pages/blob/master/LICENSE\"\u003e\u003cimg src=\"http://img.shields.io/badge/license-MIT-blue.svg?style=flat\" alt=\"License: MIT\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nElegantCalendar is an efficient and customizable full screen calendar written in SwiftUI.\n\n\u003cbr/\u003e\n\n\u003cimg src=\"https://github.com/ThasianX/GIFs/blob/master/ElegantCalendar/dark_demo.gif\" width=\"300\"/\u003e \u003cimg src=\"https://github.com/ThasianX/GIFs/blob/master/ElegantCalendar/light_demo.gif\" width=\"300\"/\u003e\n\n## [ElegantTimeline](https://github.com/ThasianX/ElegantTimeline-SwiftUI) - Shows what's possible using ElegantCalendar\n\n\u003cimg src=\"https://github.com/ThasianX/GIFs/blob/master/ElegantTimeline/demo.gif\" width=\"300\"/\u003e\n\n### Comes with 8 default themes. You can also configure your own theme. Read more to find out.\n\n\u003cp\u003e \u003cimg src=\"Screenshots/brilliantViolet.PNG\" width=\"200\"/\u003e \n    \u003cimg src=\"Screenshots/craftBrown.PNG\" width=\"200\"/\u003e \n    \u003cimg src=\"Screenshots/fluorescentPink.PNG\" width=\"200\"/\u003e \n    \u003cimg src=\"Screenshots/kiwiGreen.PNG\" width=\"200\"/\u003e \n    \u003cimg src=\"Screenshots/mauvePurple.PNG\" width=\"200\"/\u003e \n    \u003cimg src=\"Screenshots/orangeYellow.PNG\" width=\"200\"/\u003e \n    \u003cimg src=\"Screenshots/red.PNG\" width=\"200\"/\u003e \n    \u003cimg src=\"Screenshots/royalblue.PNG\" width=\"200\"/\u003e \n\u003c/p\u003e\n\n- [Introduction](#introduction)\n- [Basic Usage](#basic-usage)\n- [How It Works](#how-it-works)\n- [Customization](#customization)\n- [Use Cases](#use-cases)\n- [Demos](#demos)\n- [Installation](#installation)\n- [Requirements](#requirements)\n- [Contributing](#contributing)\n- [Resources](#resources)\n- [License](#license)\n\n## Introduction\n\n`ElegantCalendar` is inspired by [TimePage](https://us.moleskine.com/timepage/p0486) and is part of a larger repository of elegant demonstrations like this: [TimePage Clone](https://github.com/ThasianX/TimePage-Clone). It uses [ElegantPages](https://github.com/ThasianX/ElegantPages), another library I wrote specifically for paging so check that out :)\n\nIt is mainly meant to be used with apps that require the use of a calendar to function(like [ElegantTimeline](https://github.com/ThasianX/ElegantTimeline-SwiftUI)), not as a full screen date picker(the demo demonstrates how to do so if you really want to).\n\nFeatures:\n\n* Display months and years in a full screen vertical scrolling layout\n* Custom layout system that allows virtually infinite date ranges with minimal increasing memory usage\n* Customization of individual day views\n* Customization of the calendar color scheme, light and dark\n* Customization of the accessory view displayed when selecting a day\n* Excluding certain days from being selectable on the calendar\n* Scrolling to a particular day, month, or year with or without animation\n* Built in button that scrolls back to today’s month or year\n* Flexibility in either using the full calendar view that has both the monthly and yearly view or just one of the individual views\n* Haptics when performing certain actions\n* Intuitive navigation between the yearly and monthly view: swipe between views or tap on the month header to navigate to the yearly view\n* Elegant default themes\n\n\n## Basic usage\n\nUsing `ElegantCalendar` is as easy as:\n\n```swift\n\nimport ElegantCalendar\n\nstruct ExampleCalendarView: View {\n\n    // Start \u0026 End date should be configured based on your needs.\n    let startDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (-30 * 36)))\n    let endDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (30 * 36)))\n\n    @ObservedObject var calendarManager = ElegantCalendarManager(\n        configuration: CalendarConfiguration(startDate: startDate,\n                                             endDate: endDate))\n\n    var body: some View {\n        ElegantCalendarView(calendarManager: calendarManager)\n    }\n\n}\n```\n\nHowever, if you just want an individual view, not the entire calendar view, you can do either:\n\n```swift\n\nimport ElegantCalendar\n\nstruct ExampleMonthlyCalendarView: View {\n\n    // Start \u0026 End date should be configured based on your needs.\n    let startDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (-30 * 36)))\n    let endDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (30 * 36)))\n\n    @ObservedObject var calendarManager = MonthlyCalendarManager(\n        configuration: CalendarConfiguration(startDate: startDate,\n                                             endDate: endDate))\n\n    var body: some View {\n        MonthlyCalendarView(calendarManager: calendarManager)\n    }\n\n}\n\nstruct ExampleYearlyCalendarView: View {\n\n    // Start \u0026 End date should be configured based on your needs. \n    let startDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (-30 * 36)))\n    let endDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (30 * 36)))\n\n    @ObservedObject var calendarManager = YearlyCalendarManager(\n        configuration: CalendarConfiguration(startDate: startDate,\n                                             endDate: endDate))\n\n    var body: some View {\n        YearlyCalendarView(calendarManager: calendarManager)\n    }\n\n}\n\n```\n\n## How it works\n\n[`ElegantCalendarView`](https://github.com/ThasianX/ElegantCalendar/blob/master/Sources/ElegantCalendar/Views/ElegantCalendarView.swift) uses the [`ElegantHPages`](https://github.com/ThasianX/ElegantPages/blob/master/Sources/ElegantPages/Pages/Public/ElegantHPages.swift) view from [`ElegantPages`](https://github.com/ThasianX/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.\n\nLet's first talk about the monthly calendar where you can swipe up and down to see the next/previous month. This view uses [`ElegantVList`](https://github.com/ThasianX/ElegantPages/blob/master/Sources/ElegantPages/Lists/Public/ElegantVList.swift) 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. \n\nSo 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](https://github.com/warrenburton/DequeueOrNot). 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.\n\n## Customization\n\n### `ElegantCalendarManager`\n\n#### `configuration`: The configuration of the calendar view\n\n```swift \n\npublic struct CalendarConfiguration: Equatable {\n\n    let calendar: Calendar\n    let ascending: Bool // reverses the order in which the calendar is laid out\n    let startDate: Date\n    let endDate: Date\n\n}\n\n```\n\n#### `initialMonth`: The initial month to display on the calendar. If not specified, automatically defaults to the first month.\n\n#### `datasource`: The datasource of the calendar\n\n```swift \n\npublic protocol ElegantCalendarDataSource: MonthlyCalendarDataSource, YearlyCalendarDataSource { }\n\npublic protocol MonthlyCalendarDataSource {\n\n    func calendar(backgroundColorOpacityForDate date: Date) -\u003e Double\n    func calendar(canSelectDate date: Date) -\u003e Bool\n    func calendar(viewForSelectedDate date: Date, dimensions size: CGSize) -\u003e AnyView\n\n}\n\npublic protocol YearlyCalendarDataSource { }\n\n```\n\nThis 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.\n\n#### `delegate`: The delegate of the calendar\n\n```swift \n\npublic protocol ElegantCalendarDelegate: MonthlyCalendarDelegate, YearlyCalendarDelegate { }\n\npublic protocol MonthlyCalendarDelegate {\n\n    func calendar(didSelectDay date: Date)\n    func calendar(willDisplayMonth date: Date)\n\n}\n\npublic protocol YearlyCalendarDelegate {\n\n    func calendar(didSelectMonth date: Date)\n    func calendar(willDisplayYear date: Date)\n\n}\n\n```\n\nThis 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.\n\n#### `theme`: The theme of various components of the calendar. Default is royal blue. Available for `ElegantCalendarView` \u0026 `YearlyCalendarView` \u0026 `MonthlyCalendarView`.\n\n```swift\n\npublic struct CalendarTheme: Equatable, Hashable {\n\n    let primary: Color\n\n}\n\npublic extension CalendarTheme {\n\n    static let brilliantViolet = CalendarTheme(primary: .brilliantViolet)\n    static let craftBrown = CalendarTheme(primary: .craftBrown)\n    static let fluorescentPink = CalendarTheme(primary: .fluorescentPink)\n    static let kiwiGreen = CalendarTheme(primary: .kiwiGreen)\n    static let mauvePurple = CalendarTheme(primary: .mauvePurple)\n    static let orangeYellow = CalendarTheme(primary: .orangeYellow)\n    static let red = CalendarTheme(primary: .red)\n    static let royalBlue = CalendarTheme(primary: .royalBlue)\n\n}\n\nElegantCalendarView(...)\n    .theme(.mauvePurple)\n\n```\n\nTo 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.\n\n#### `horizontal` or `vertical`: The orientation of the calendar. The default is `horizontal`, as shown in the GIF. Available for `ElegantCalendarView` \u0026 `YearlyCalendarView` \u0026 `MonthlyCalendarView`. \n\n```swift\n\nElegantCalendarView(...)\n    .vertical()\n\n```\n\n#### `allowsHaptics`: Whether haptics is enabled or not. Default is enabled. Available for `ElegantCalendarView` \u0026 `MonthlyCalendarView`\n\n```swift\n\nElegantCalendarView(...)\n    .allowsHaptics(false)\n\n```\n\nUsers get haptics whenever they tap a day, scroll to a new month, or press the scroll back to today button.\n\n#### `frame`: Custom width for the monthly calendar view. Available for `MonthlyCalendarView`\n\n```swift\n\nMonthlyCalendarView(...)\n    .frame(width: ...)\n\n```\n\n## Use Cases\n\nThe following aspects of `ElegantCalendarManager` can be used:\n\n`var currentMonth: Date` - The current month displayed on the calendar view.\n\n`var selectedDate: Date?` - The date selected on the calendar view, if any.\n\n`var isShowingYearView: Bool` - Whether the year view is showing. If false, the month view is showing.\n\n`func scrollToMonth(_ month: Date, animated: Bool = true)` - Scroll back to a certain month, animated or not. No date is selected in the process.\n\n`func scrollBackToToday(animated: Bool = true)` - Scroll back to today, animated or not. Today's date is selected in the process.\n\n`func scrollToDay(_ day: Date, animated: Bool = true)` - Scroll back to a certain date, animated or not. The date is selected in the process.\n\n## Demos\n\nThe demos shown in the GIF can be checked out on [example repo](https://github.com/ThasianX/ElegantCalendar/tree/master/Example).\n\n## Installation\n\n`ElegantCalendar` is available using the [Swift Package Manager](https://swift.org/package-manager/):\n\nUsing Xcode 11, go to `File -\u003e Swift Packages -\u003e Add Package Dependency` and enter https://github.com/ThasianX/ElegantCalendar\n\nIf you are using `Package.swift`, you can also add `ElegantCalendar` as a dependency easily.\n\n```swift\n\nlet package = Package(\n  name: \"TestProject\",\n  dependencies: [\n    .package(url: \"https://github.com/ThasianX/ElegantCalendar\", from: \"4.2.0\")\n  ],\n  targets: [\n    .target(name: \"TestProject\", dependencies: [\"ElegantCalendar\"])\n  ]\n)\n\n```\n\nInside whatever app is using `ElegantCalendar` or your `Swift Package` that uses `ElegantCalendar` as a dependency:\n\n1) Scroll the project navigator down to the `Swift Package Dependencies` section. Inside `ElegantCalendar`, you'll see a directory called `ElegantCalendar.xcassets`. \n2) After you've located it, open your project's settings and navigate to your target's build phases in a parallel window. \n3) 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](https://github.com/apple/swift-evolution/blob/master/proposals/0271-package-manager-resources.md).\n4) 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`.\n\nIf you don't know how to do this, refer to the `Demo`.\n\n## Requirements\n\n- iOS 13.0+\n- Xcode 11.0+\n\n## Contributing\n\nIf 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](https://github.com/ThasianX/ElegantCalendar/issues) 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](https://github.com/ThasianX/ElegantCalendar/issues/new).\n\n## Resources\n\nAlso, here's a [dump of resources](resources.txt) I found useful when working on this\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FThasianX%2FElegantCalendar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FThasianX%2FElegantCalendar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FThasianX%2FElegantCalendar/lists"}