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.

Last update: Jun 15, 2022

HorizonCalendar

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

Swift Package Manager compatible Carthage compatible Version License Platform Swift

Introduction

HorizonCalendar is UIKit library for displaying a range of dates in a vertically-scrolling or horizontally-scrolling calendar component. Its declarative API makes updating the calendar straightforward, while also providing many customization points to support a diverse set of designs and use cases.

Features:

  • Supports all calendars from Foundation.Calendar (Gregorian, Japanese, Hebrew, etc.)
  • Display months in a vertically-scrolling or horizontally-scrolling layout
  • Declarative API that enables unidirectional data flow for updating the content of the calendar
  • A custom layout system that enables virtually infinite date ranges without increasing memory usage
  • Pagination for horizontally-scrolling calendars
  • Specify custom views for individual days, month headers, and days of the week
  • Specify custom views to highlight date ranges
  • Specify custom views to overlay parts of the calendar, enabling features like tooltips
  • A day selection handler to monitor when a day is tapped
  • Customizable layout metrics
  • Pin the days-of-the-week row to the top
  • Show partial boundary months (exactly 2020-03-14 to 2020-04-20, for example)
  • Scroll to arbitrary dates and months, with or without animation
  • Robust accessibility support
  • Inset the content without affecting the scrollable region using UIView layout margins
  • Separator below the days-of-the-week row
  • Right-to-left layout support

HorizonCalendar serves as the foundation for the date pickers and calendars used in Airbnb's highest trafficked flows.

Search Stays Availability Calendar Wish List Experience Reservation Experience Host Calendar Management
Search Stay Availability Calendar Wish List Experience Reservation Experience Host Calendar Management

Table of Contents

Example App

An example app is available to showcase and enable you to test some of HorizonCalendar's features. It can be found in ./Example/HorizonCalendarExample.xcworkspace.

Note: Make sure to use the .xcworkspace file, and not the .xcodeproj file, as the latter does not have access to HorizonCalendar.framework.

Demos

The example app has several demo view controllers to try, with both vertical and horizontal layout variations:

Demo Picker

Single Day Selection

Vertical Horizontal
Single Day Selection Vertical Single Day Selection Horizontal

Day Range Selection

Vertical Horizontal
Day Range Selection Vertical Day Range Selection Horizontal

Selected Day Tooltip

Vertical Horizontal
Selected Day Tooltip Vertical Selected Day Tooltip Horizontal

Scroll to Day with Animation

Vertical Horizontal
Scroll to Day with Animation Vertical Scroll to Day with Animation Horizontal

Integration Tutorial

Requirements

  • Deployment target iOS 11.0+
  • Swift 5+
  • Xcode 10.2+

Installation

Swift Package Manager

To install HorizonCalendar using Swift Package Manager, add .package(name: "HorizonCalendar", url: "https://github.com/airbnb/HorizonCalendar.git", from: "1.0.0")," to your Package.swift, then follow the integration tutorial here.

Carthage

To install HorizonCalendar using Carthage, add github "airbnb/HorizonCalendar" to your Cartfile, then follow the integration tutorial here.

CocoaPods

To install HorizonCalendar using CocoaPods, add pod 'HorizonCalendar' to your Podfile, then follow the integration tutorial here.

Building a CalendarView

Once you've installed HorizonCalendar into your project, getting a basic calendar working is just a few steps.

Basic Setup

Importing HorizonCalendar

At the top of the file where you'd like to use HorizonCalendar (likely a UIView or UIViewController subclass), import HorizonCalendar:

import HorizonCalendar 

Initializing a CalendarView with CalendarViewContent

CalendarView is the UIView subclass that renders the calendar. All visual aspects of CalendarView are controlled through a single type - CalendarViewContent. To create a basic CalendarView, you initialize one with an initial CalendarViewContent:

let calendarView = CalendarView(initialContent: makeContent())
private func makeContent() -> CalendarViewContent {
  let calendar = Calendar.current

  let startDate = calendar.date(from: DateComponents(year: 2020, month: 01, day: 01))!
  let endDate = calendar.date(from: DateComponents(year: 2021, month: 12, day: 31))!

  return CalendarViewContent(
    calendar: calendar,
    visibleDateRange: startDate...endDate,
    monthsLayout: .vertical(options: VerticalMonthsLayoutOptions()))
}

At a minimum, CalendarViewContent must be initialized with a Calendar, a visible date range, and a months layout (either vertical or horizontal). The visible date range will be interpreted as a range of days using the Calendar instance passed in for the calendar parameter.

For this example, we're using a Gregorian calendar, a date range of 2020-01-01 to 2021-12-31, and a vertical months layout.

Make sure to add calendarView as a subview, then give it a valid frame either using Auto Layout or by manually setting its frame property. If you're using Auto Layout, note that CalendarView does not have an intrinsic content size.

view.addSubview(calendarView)

calendarView.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
  calendarView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
  calendarView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
  calendarView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
  calendarView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor),
])

At this point, building and running your app should result in something that looks like this:

Basic Calendar

Customizing CalendarView

Providing a custom view for each day

HorizonCalendar comes with default views for month headers, day of week items, and day items. You can also provide custom views for each of these item types, enabling you to display whatever custom content makes sense for your app.

Since all visual aspects of CalendarView are configured through CalendarViewContent, we'll expand on our makeContent function. Let's start by providing a custom view for each day in the calendar:

private func makeContent() -> CalendarViewContent {
  return CalendarViewContent(
    calendar: calendar,
    visibleDateRange: today...endDate,
    monthsLayout: .vertical(VerticalMonthsLayoutOptions()))
    
    .withDayItemModelProvider { day in
      // Return a `CalendarItemModel` representing the view for each day
    }
}

The withDayItemModelProvider(_:) function on CalendarViewContent returns a new CalendarViewContent instance with the custom day item model provider configured. This function takes a single parameter - a provider closure that returns a CalendarItemModel for a given Day.

CalendarItemModel is a type that abstracts away the creation and configuration of a UIView. It's generic over a ViewRepresentable type, which can be any type conforming to CalendarItemViewRepresentable. You can think of CalendarItemViewRepresentable as a blueprint for creating and updating instances of a particular type of view to be displayed in the calendar. For example, if we want to use a UILabel for our custom day view, we'll need to create a type that knows how to create and update that label. Here's a simple example:

import HorizonCalendar

struct DayLabel: CalendarItemViewRepresentable {

  /// Properties that are set once when we initialize the view.
  struct InvariantViewProperties: Hashable {
    let font: UIFont
    let textColor: UIColor
    let backgroundColor: UIColor
  }

  /// Properties that will vary depending on the particular date being displayed.
  struct ViewModel: Equatable {
    let day: Day
  }

  static func makeView(
    withInvariantViewProperties invariantViewProperties: InvariantViewProperties)
    -> UILabel
  {
    let label = UILabel()

    label.backgroundColor = invariantViewProperties.backgroundColor
    label.font = invariantViewProperties.font
    label.textColor = invariantViewProperties.textColor

    label.textAlignment = .center
    label.clipsToBounds = true
    label.layer.cornerRadius = 12
    
    return label
  }

  static func setViewModel(_ viewModel: ViewModel, on view: UILabel) {
    view.text = "\(viewModel.day)"
  }

}

CalendarItemViewRepresentable requires us to implement a static makeView function, which should create and return a view given a set of invariant view properties. We want our label to have a configurable font and text color, so we've made those configurable via the InvariantViewProperties type. In our makeView function, we use those invariant view properties to create and configure an instance of our label.

CalendarItemViewRepresentable also requires us to implement a static setViewModel function, which should update all data-dependent properties (like the day text) on the provided view.

Now that we have a type conforming to CalendarItemViewRepresentable, we can use it to create a CalendarItemModel to return from the day item model provider:

  return CalendarViewContent(...)

    .withDayItemModelProvider { day in
      CalendarItemModel<DayLabel>(
        invariantViewProperties: .init(
          font: UIFont.systemFont(ofSize: 18), 
          textColor: .darkGray,
          backgroundColor: .clear)
        viewModel: .init(day: day))
    }

Similar item model provider functions are available to customize the views used for month headers, day-of-the-week items, and more.

If you build and run your app, it should now look like this:

Custom Day Views

Adjusting layout metrics

We can also use CalendarViewContent to adjust layout metrics. We can improve the layout of our current CalendarView by adding some additional spacing between individual days and months:

  return CalendarViewContent(...)
    .withDayItemModelProvider { ... }

    .withInterMonthSpacing(24)
    .withVerticalDayMargin(8)
    .withHorizontalDayMargin(8)

Just like when we configured a custom day view via the day item provider, changes to layout metrics are also done through CalendarViewContent. withInterMonthSpacing(_:), withVerticalDayMargin(_:), and withHorizontalDayMargin(_:) each return a mutated CalendarViewContent with the corresponding layout metric value updated, enabling you to chain function calls together to produce a final content instance.

After building and running your app, you should see a much less cramped layout:

Custom Layout Metrics

Adding a day range indicator

Day range indicators are useful for date pickers that need to highlight not just individual days, but ranges of days. HorizonCalendar offers an API to do exactly this via the CalendarViewContent function withDayRangeItemModelProvider(for:_:). Similar to what we did for our custom day item model provider, for day ranges, we need to provide a CalendarItemModel for each day range we want to highlight.

First, we need to create a ClosedRange<Date> that represents the day range for which we'd like to provide a CalendarItemModel. The Dates in our range will be interpreted as Days using the Calendar instance with which we initialized our CalendarViewContent.

  let lowerDate = calendar.date(from: DateComponents(year: 2020, month: 01, day: 20))!
  let upperDate = calendar.date(from: DateComponents(year: 2020, month: 02, day: 07))!
  let dateRangeToHighlight = lowerDate...upperDate

Next, we need to invoke the withDayRangeItemModelProvider(for:_:) on our CalendarViewContent:

  return CalendarViewContent(...)
    ...
    
    .withDayRangeItemModelProvider(for: [dateRangeToHighlight]) { dayRangeLayoutContext in 
      // Return a `CalendarItemModel` representing the view that highlights the entire day range
    }

For each day range derived from the Set<ClosedRange<Date>> passed into this function, our day range item model provider closure will be invoked with a context instance that contains all of the information needed for us to render a view to be used to highlight a particular day range. Here is an example implementation of such a view:

import UIKit

final class DayRangeIndicatorView: UIView {

  private let indicatorColor: UIColor

  init(indicatorColor: UIColor) {
    self.indicatorColor = indicatorColor
    super.init(frame: frame)
    backgroundColor = .clear
  }

  required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }

  var framesOfDaysToHighlight = [CGRect]() {
    didSet {
      guard framesOfDaysToHighlight != oldValue else { return }
      setNeedsDisplay()
    }
  }

  override func draw(_ rect: CGRect) {
    let context = UIGraphicsGetCurrentContext()
    context?.setFillColor(indicatorColor.cgColor)

    // Get frames of day rows in the range
    var dayRowFrames = [CGRect]()
    var currentDayRowMinY: CGFloat?
    for dayFrame in framesOfDaysToHighlight {
      if dayFrame.minY != currentDayRowMinY {
        currentDayRowMinY = dayFrame.minY
        dayRowFrames.append(dayFrame)
      } else {
        let lastIndex = dayRowFrames.count - 1
        dayRowFrames[lastIndex] = dayRowFrames[lastIndex].union(dayFrame)
      }
    }

    // Draw rounded rectangles for each day row
    for dayRowFrame in dayRowFrames {
      let roundedRectanglePath = UIBezierPath(roundedRect: dayRowFrame, cornerRadius: 12)
      context?.addPath(roundedRectanglePath.cgPath)
      context?.fillPath()
    }
  }

}

Next, we need a type that conforms to CalendarItemViewRepresentable that knows how to create and update instances of DayRangeIndicatorView. To make things easy, we can just make our view conform to this protocol:

import HorizonCalendar

extension DayRangeIndicatorView: CalendarItemViewRepresentable {

  struct InvariantViewProperties: Hashable {
    let indicatorColor = UIColor.blue.withAlphaComponent(0.15)
  }

  struct ViewModel: Equatable {
    let framesOfDaysToHighlight: [CGRect]
  }

  static func makeView(
    withInvariantViewProperties invariantViewProperties: InvariantViewProperties)
    -> DayRangeIndicatorView
  {
    DayRangeIndicatorView(indicatorColor: invariantViewProperties.indicatorColor)
  }

  static func setViewModel(_ viewModel: ViewModel, on view: DayRangeIndicatorView) {
    view.framesOfDaysToHighlight = viewModel.framesOfDaysToHighlight
  }

}

Last, we need to return a CalendarItemModel representing our DayRangeIndicatorView from the day range item model provider closure:

  return CalendarViewContent(...)
    ...
    
    .withDayRangeItemModelProvider(for: [dateRangeToHighlight]) { dayRangeLayoutContext in
      CalendarItemModel<DayRangeIndicatorView>(
        invariantViewProperties: .init(indicatorColor: UIColor.blue.withAlphaComponent(0.15)),
        viewModel: .init(framesOfDaysToHighlight: dayRangeLayoutContext.daysAndFrames.map { $0.frame }))
    }

If you build and run the app, you should see a day range indicator view that highlights 2020-01-20 to 2020-02-07:

Day Range Indicator

Adding a tooltip

HorizonCalendar provides an API to overlay parts of the calendar with custom views. One use case that this enables is adding tooltips to certain days - a feature that's used in the Airbnb app to inform users when their checkout date must be a certain number of days in the future from their check-in date.

First, we need to decide on the locations of the items that we'd like to overlay with our own custom view. We can overlay a day or a monthHeader - the two cases available on CalendarViewContent.OverlaidItemLocation. Let's overlay the day at 2020-01-15:

  let dateToOverlay = calendar.date(from: DateComponents(year: 2020, month: 01, day: 15))!
  let overlaidItemLocation: CalendarViewContent.OverlaidItemLocation = .day(containingDate: dateToOverlay) 

Like all other customizations, we'll add an overlay by calling a function on our CalendarViewContent instance that configures an overlay item model provider closure:

  return CalendarViewContent(...)
    ...
    
    .withOverlayItemModelProvider(for: [overlaidItemLocation]) { overlayLayoutContext in
      // Return a `CalendarItemModel` representing the view to use as an overlay for the overlaid item location
    }

For each overlaid item location in the Set<CalendarViewContent.OverlaidItemLocation> passed into this function, our overlay item model provider closure will be invoked with a context instance that contains all of the information needed for us to render a view to be used as an overlay for a particular overlaid item location. Here is an example implementation of a tooltip overlay view:

import UIKit

final class TooltipView: UIView {

  init(backgroundColor: UIColor, borderColor: UIColor, font: UIFont, textColor: UIColor) {
    super.init(frame: .zero)

    backgroundView.backgroundColor = backgroundColor
    backgroundView.layer.borderColor = borderColor
    addSubview(backgroundView)

    label.font = font
    label.textColor = textColor
    addSubview(label)
  }

  required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
  
  var text: String {
    get { label.text ?? "" }
    set { label.text = newValue }
  }

  var frameOfTooltippedItem: CGRect? {
    didSet {
      guard frameOfTooltippedItem != oldValue else { return }
      setNeedsLayout()
    }
  }

  override func layoutSubviews() {
    super.layoutSubviews()

    guard let frameOfTooltippedItem = frameOfTooltippedItem else { return }

    label.sizeToFit()
    let labelSize = CGSize(
      width: min(label.bounds.size.width, bounds.width),
      height: label.bounds.size.height)

    let backgroundSize = CGSize(width: labelSize.width + 16, height: labelSize.height + 16)

    let proposedFrame = CGRect(
      x: frameOfTooltippedItem.midX - (backgroundSize.width / 2),
      y: frameOfTooltippedItem.minY - backgroundSize.height - 4,
      width: backgroundSize.width,
      height: backgroundSize.height)

    let frame: CGRect
    if proposedFrame.maxX > bounds.width {
      frame = proposedFrame.applying(.init(translationX: bounds.width - proposedFrame.maxX, y: 0))
    } else if proposedFrame.minX < 0 {
      frame = proposedFrame.applying(.init(translationX: -proposedFrame.minX, y: 0))
    } else {
      frame = proposedFrame
    }

    backgroundView.frame = frame
    label.center = backgroundView.center
  }

  // MARK: Private

  private lazy var backgroundView: UIView = {
    let view = UIView()
    view.layer.borderWidth = 1
    view.layer.cornerRadius = 6
    view.layer.shadowColor = UIColor.black.cgColor
    view.layer.shadowOpacity = 0.8
    view.layer.shadowOffset = .zero
    view.layer.shadowRadius = 8
    return view
  }()

  private lazy var label: UILabel = {
    let label = UILabel()
    label.textAlignment = .center
    label.lineBreakMode = .byTruncatingTail
    return label
  }()

}

Next, we need a type that conforms to CalendarItemViewRepresentable that knows how to create and update instances of TooltipView. To make things easy, we can just make our view conform to this protocol:

import HorizonCalendar

extension TooltipView: CalendarItemViewRepresentable {

  struct InvariantViewProperties: Hashable {
    let backgroundColor: UIColor
    let borderColor: UIColor
    let font: UIFont
    let textColor: UIColor
  }

  struct ViewModel: Equatable {
    let frameOfTooltippedItem: CGRect?
    let text: String
  }

  static func makeView(
    withInvariantViewProperties invariantViewProperties: InvariantViewProperties)
    -> TooltipView
  {
  TooltipView(
    borderColor: invariantViewProperties.borderColor, 
    font: invariantViewProperties.font, 
    textColor: invariantViewProperties.textColor)
  }

  static func setViewModel(_ viewModel: ViewModel, on view: TooltipView) {
    view.frameOfTooltippedItem = viewModel.frameOfTooltippedItem
    view.text = viewModel.text
  }

}

Last, we need to return a CalendarItemModel representing our TooltipView from the overlay item model provider closure:

  return CalendarViewContent(...)
    ...
    
    .withOverlayItemModelProvider(for: [overlaidItemLocation]) { overlayLayoutContext in
      CalendarItemModel<TooltipView>(
        invariantViewProperties: .init(
          backgroundColor: .white, 
          borderColor: .black, 
          font: UIFont.systemFont(ofSize: 16), 
          textColor: .black),
        viewModel: .init(
          frameOfTooltippedItem: overlayLayoutContext.overlaidItemFrame, 
          text: "Dr. Martin Luther King Jr.'s Birthday"))
    }

If you build and run the app, you should see a tooltip view hovering above 2020-01-15:

Tooltip View

Responding to day selection

If you're building a date picker, you'll most likely need to respond to the user tapping on days in the calendar. To do this, provide a day selection handler closure via CalendarView's daySelectionHandler:

calendarView.daySelectionHandler = { [weak self] day in
  self?.selectedDay = day
}
private var selectedDay: Day?

The day selection handler closure is invoked whenever a day in the calendar is selected. You're provided with a Day instance for the day that was selected. If we want to highlight the selected day once its been tapped, we'll need to create a new CalendarViewContent with a day calendar item model that looks different for the selected day:

  let selectedDay = self.selectedDay

  return CalendarViewContent(...)

    .withDayItemModelProvider { day in
      var invariantViewProperties = DayLabel.InvariantViewProperties(
        font: UIFont.systemFont(ofSize: 18), 
        textColor: .darkGray,
        backgroundColor: .clear)

      if day == selectedDay {
        invariantViewProperties.textColor = .white
        invariantViewProperties.backgroundColor = .blue
      }
      
      return CalendarItemModel<DayLabel>(
        invariantViewProperties: invariantViewProperties,
        viewModel: .init(day: day))
  }

Last, we'll change our day selection handler so that it not only stores the selected day, but also sets an updated content instance on calendarView:

calendarView.daySelectionHandler = { [weak self] day in
  guard let self = self else { return }
  
  self.selectedDay = day
  
  let newContent = self.makeContent()
  self.calendarView.setContent(newContent)
}

After building and running the app, tapping days should cause them to turn blue:

Day Selection

Technical Details

If you'd like to learn about how HorizonCalendar was implemented, check out the Technical Details document. It provides an overview of HorizonCalendar's architecture, along with information about why it's not implemented using UICollectionView.

Contributions

HorizonCalendar welcomes fixes, improvements, and feature additions. If you'd like to contribute, open a pull request with a detailed description of your changes.

As a rule of thumb, if you're proposing an API-breaking change or a change to existing functionality, consider proposing it by opening an issue, rather than a pull request; we'll use the issue as a public forum for discussing whether the proposal makes sense or not. See CONTRIBUTING for more details.

Authors

Bryan Keller

Maintainers

Bryan Keller

Bryn Bodayle

If you or your company has found HorizonCalendar to be useful, let us know!

License

HorizonCalendar is released under the Apache License 2.0. See LICENSE for details.

GitHub

https://github.com/airbnb/HorizonCalendar
Comments
  • 1. [feat] Pagination for Horizontal Calendars

    Hey Contributors, just wanted to say thanks!

    I have actually already switched over to using HorizonCalendar in one of our applications that required a vertical calendar implementation and I must say, it is one of the easiest declarative calendar options available,. so much so that we're starting to move over another application over to it.

    However, we require a horizontal calendar, with pagination. I went and attempted to set isPagingEnabled to the underlying UIScrollView, but that definitely lead to some bizarre behavior (specifically around the width I believe), and the pagination isn't true.

    Is there any hint at adding an official way to set pagination for horizontal month layouts?

    Reviewed by saudeon at 2020-11-12 02:21
  • 2. Question: Center Horizontal Calendar

    Hey Guys,

    Don't know if this belongs here, but I am having troubling finding a way to center the horizontal calendar, as opposed to aligning it to the left, as the default implementation suggests. In the same vein, I am also looking for the correct way to center the Month Title (August 2020) in the center of the screen, rather than aligning it to the left as well. Basically just trying to show only one month at a time with equal padding to the trailing and leading edge of the calendar, as well as center the Month Title. My apologies if these are already features and I am just missing them. I have attached two screenshots to demonstrate what I mean in regards to the alignment issue, with one being what I currently have and one being my desired outcome.

    Edit: I did end up figuring out the centered header by working off the default implementation, but needed to basically copy to my source code the extended function "firstDate" since it was internally protected. Is there an easier way around this?

    Thanks

    Screen Shot 2020-08-08 at 5 04 35 PM

    Screen Shot 2020-08-08 at 5 04 22 PM

    Reviewed by zachterrell57 at 2020-08-08 22:13
  • 3. Calendar not being refreshed when new events are fetched from the sever

    First of all, thanks for this amazing library.

    I've noticed the following issue when using the library, the data is not being refreshed properly after fetching data from the server. I am attaching screenshots at the bottom.

    example:

      var events: [String: Any] = [:]
    
      var presenter: CalendarPresenterDelegate!
    
      override func viewDidLoad() {
          super.viewDidLoad()
          /// some networking logic
          presenter.fetchEvents()
      }
    
      override func makeContext() -> CalendarViewContent {
    
            let startDate = Date()
            let endDate = calendar.date(from: DateComponents(year: 2021, month: 12, day: 31))!
    
            return CalendarViewContent(calendar: calendar,
                                       visibleDateRange: startDate...endDate,
                                       monthsLayout: .horizontal(monthWidth: min(view.frame.height, view.frame.width) - 40))
                .withDayItemProvider { day -> CalendarItem<EventLabel, Day> in
                    let date = self.calendar.date(from: day.components)!
                    let isSelectedDay = self.selectedDates.contains(date)
                    return CalendarItem<EventLabel, Day>(
                        viewModel: day,
                        styleID: isSelectedDay ? "SelectedDayLabelStyle" : "DayLabelStyle",
                        buildView: {
                            return EventLabel()
                        },
                        updateViewModel: self.setupEvent)
                }
                .withInterMonthSpacing(16)
                .withVerticalDayMargin(8)
                .withHorizontalDayMargin(8)
        }
    
        func setEvents(events: [String: Any]) {
            self.events = events
            let newContent = makeContent()
            calendarView.setContent(newContent)
        }
    
        override func setupEvent(label: EventLabel, day: Day) {
    
            label.text = "\(day.day)"
            // clear the events
            label.hasNoEvents()
    
            if isSelectedDay {
                label.backgroundColor = Colors.colorAccent
            } else {
                label.backgroundColor = isToday ? Colors.colorPrimary : .clear
            }
    
            guard let object = getEvents(date: date) else {
                return
            }
    
            if (object["event1"] as? Bool) == true {
                label.hasEvent1()
                label.textColor = .lightGray
                return
            }
    
            if (object["event2"] as? [String])?.isEmpty == false {
                label.hasEvent2()
            }
        }
    

    This is what we get after setEvents being called, however the result we need is in the second image: image 1 Screen Shot 2020-07-14 at 7 11 14 PM image 2 Screen Shot 2020-07-14 at 7 11 00 PM

    PS: if we scroll to September and back the UI refreshes and the UI updates properly. it seems as if the calendarView.setContent(newContent) is not setting the content properly.

    Reviewed by mustiikhalil at 2020-07-14 16:16
  • 4. Check In and Check Out custom time for highlighting days

    Hi Airbnb Team,

    This is my second time asking question.

    1. Is it possible specify time ie. hours for check in and out highlighting as in screenshot.
    Screen Shot 2021-01-29 at 10 58 04 AM
    1. Can I make bigger in size for check in guest images? Similar in screenshot! Overall is it even achievable as design in airbnb calendar?

    Thank you for your time!

    Reviewed by bekzodrakhmatof at 2021-01-29 02:05
  • 5. Make sure it also works with tilted screens

    If one turns on the other screen orientations for the device and run the example, this calendar is not rendering properly anymore. Make sure that those device orientations are supported.

    Reviewed by ruurd at 2020-06-20 09:35
  • 6. Horizontal Calendar Leading and Trailing sizes are different

    Hi Airbnb Team,

    Thank you for applying pagination to the calendar.

    For some reason my calendar leading and trailing spaces are different. I want to use full screen width for horizontal calendar.

    See the attached image.

    IMG_0649

    Thank you for the answer!

    Reviewed by bekzodrakhmatof at 2021-02-01 08:58
  • 7. Multiple dates in a month are blanked out.

    While leaving default code, certain dates in each month are blank. When selected they are show but otherwise they are blank... this issue does not occur to other unselected dates.

    Reviewed by isaacthedeveloper at 2020-08-19 21:15
  • 8. Ability to configure scrolling behaviour

    Hey,

    first of all, thank you for open-sourcing this neat library!

    Secondly, I wanted to ask you: would you consider adding some kind of ability to configure scrolling behaviour (perhaps to MonthsLayout enum via LayoutOptions)? Because I wanted to implement horizontal calendar with snapping like behaviour (where you have single month shown on the screen and then using swipe gesture or buttons move to next/previous month) and the only way how I managed to achieve that was by going into view hierarchy and disabling scroll on NoContentInsetAdjustmentScrollView which feels kinda hacky as it would stop working if at some point you decide to move scrollview somewhere else.

    Also, I was curious: does HorizonCalendar support RTL layout? Because in order to achieve proper scrolling on RTL I had to change transform of CalendarView

    Thanks

    Reviewed by Pattio at 2020-07-10 13:16
  • 9. How to set the size of day items?

    I'm having a hard time understanding how to set the size of items to get a more compact month.

    When the horizontal/vertical day margins are 0, they end up being huge - like 80pt squares. When I set the margins to be 32, then they're appropriately sized, but they're still taking up the same space (because of the margins)

    How do I configure the calendar so that the items are smaller, like 32 with 0 margins?

    Reviewed by Mordil at 2021-09-10 05:19
  • 10. When taping a date, The calendar view zooms in, and the month header disappears.

    Hi, Loving the clean look of the calendar.. You did a great job. I have an issue, when I tap a date on the calendar, the calendar slightly zooms in so it covers the month header.... any idea why that is happening? Also is there a way I can make the header bold?

    Reviewed by isaachballas at 2020-09-17 21:39
  • 11. Unwanted extra trailing spacing/insets

    I'm having weird issue where calendar is insetted by 10ish pixels on right side by default. As you can see in picture below, highlighted view is calendarView's scrollView, and on right side after each row of items there is small spacing.

    Only spacing related thing i'm changing is .withInterMonthSpacing(16). I have custom view for DayView but same issue happens if I just use default withDayItemModelProvider.

    Also, CalendarView is added as subview and it has 4 constraints to superview (leading, trailing, top and bottom).
    White spacing seen on sides is from superview's leading/trailing constraints so it shouldn't affect calendar. I've also tried removing that space but same issue appears.

    Reviewed by UrosZivaljevic at 2020-09-04 12:14
  • 12. How can I change the text color in dayRangeItemProvider

    It's pretty obvious to me how to change the text color for single date selection, since the example CalendarItemViewRepresentable makeView method just returns a UILabel. However, in the example DayRangeIndicatorView, it draws a bezier curve and I can't tell at all where the text comes from or how to customize it. Any tips or example would be much apppreciated!

    Reviewed by smaccoun at 2022-03-15 21:30
  • 13. How to add multiple types of ranges (overlapping ranges with different layout)?

    In my app's case we have to add some overlapping ranges but I couldn't find a way to do so.

    I think there should be a way to have multiple types of ranges (even overlapping ones).

    I have already done a very plain implementation that works for me but I would like to start a discussion here before openning a PR so that I gather more requirements on how this should be done.

    Reviewed by tzatzosm at 2022-03-04 14:08
  • 14. Breaking constraints

    Hello! I have an issue with creating my own custom dayView, it's called "CalendarDayView". It seems that some of constraints inside of your calendar view became broken after adding my CalendarDayView. Please help!

    `Will attempt to recover by breaking constraint <NSLayoutConstraint:0x600000f065d0 H:[UIView:0x151d79770]-(1)-| (active, names: '|':ReportingSystem.CalendarDayView:0x151d795f0 )>

    Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful. 2021-10-25 16:43:05.072944+0300 ReportingSystem[6276:181178] [LayoutConstraints] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) ( "<NSAutoresizingMaskLayoutConstraint:0x600000f783c0 h=--& v=--& ReportingSystem.CalendarDayView:0x151d33a70.height == 0 (active)>", "<NSLayoutConstraint:0x600000f03610 V:|-(2)-[UIView:0x151d2abc0] (active, names: '|':ReportingSystem.CalendarDayView:0x151d33a70 )>", "<NSLayoutConstraint:0x600000f036b0 V:[UIView:0x151d2abc0]-(2)-| (active, names: '|':ReportingSystem.CalendarDayView:0x151d33a70 )>" )`

    Снимок экрана 2021-10-25 в 16 43 41

    Reviewed by StewieGilliganGriffin at 2021-10-25 13:51
Simple customizable calendar component in Swift :calendar:
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,

Jun 13, 2022
A fully customizable calendar view acting as a date range picker
A fully customizable calendar view acting as a date range picker

Demo Installation CocoaPods With CocoaPods you can simply add GLCalendarView in your Podfile: pod "GLCalendarView", "~> 1.0.0" Source File You can co

May 28, 2022
The Unofficial Apple iOS Swift Calendar View. Swift calendar Library. iOS calendar Control. 100% Customizable
The Unofficial Apple iOS Swift Calendar View. Swift calendar Library. iOS calendar Control. 100% Customizable

Q: How will my calendar dateCells look with this library? A: However you want them to look. More Images Features Range selection - select dates in a r

Jun 24, 2022
Full featured lunar calendar library
Full featured lunar calendar library

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

Jun 10, 2022
An easy to use SwiftUI date picker for Shamsi (Persian) calendar
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

Apr 22, 2022
SwiftUI Simple Calendar / Date Picker for iOS
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

May 28, 2022
A nicer iOS UI component for picking date and time
A nicer iOS UI component for picking date and time

DateTimePicker A nicer iOS UI component for picking date and time. Features Date and Time Picker / Date Picker only / Time Picker only - your choice!

Jun 18, 2022
iOS 7+ Calendar (Date Picker) with Infinite Scrolling.
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

Jun 14, 2022
A library that expresses a github contribution calendar through an array of dates. Supports iOS and macOS.
A library that expresses a github contribution calendar through an array of dates. Supports iOS and macOS.

A library that expresses a github contribution calendar through an array of dates. Supports iOS and macOS.

Jun 21, 2022
A SwiftUI calendar view that allows month switching and date picking.
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

Jun 18, 2022
A fully customizable iOS calendar library, compatible with Objective-C and Swift
A fully customizable iOS calendar library, compatible with Objective-C and Swift

Table of contents Screenshots Installation Pre-knowledge Support Contact Screenshots iPhone iPad Safe Orientation Today Extension iOS8/9 iOS10 Interac

Jun 27, 2022
Calendar component with RTL languages written in swift
Calendar component with RTL languages written in swift

GDCalendar Calendar component with both RTL/LTR languages support with Swipe Gesture enabled navigation. Easy to use with Storyboard and Attributes In

Aug 21, 2021
A Swift UI component for Lunar Calendar Picker
A Swift UI component for Lunar Calendar Picker

LunarYearDatePicker a Swift UI component for Lunar Calendar Picker Usage: struct

Jan 2, 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

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.

May 16, 2022
An Easy to Use Calendar for iOS (Swift 5.0)
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

Jun 17, 2022
SwiftUICalendar - SwiftUI simple calendar
SwiftUICalendar - SwiftUI simple calendar

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

Jun 18, 2022
Dead simple calendar implementation
Dead simple calendar implementation

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

Dec 23, 2021
Calendar View - It's lightweight and simple control with supporting Locale and CalendarIdentifier.
Calendar View - It's lightweight and simple control with supporting Locale and CalendarIdentifier.

iOS Calendar It's lightweight and simple control with supporting Locale and CalendarIdentifier. There're samples for iPhone and iPad, and also with us

Apr 18, 2022
An Event View based on Apple's Event Detail View. Written in Swift 3. Supports ARC, Autolayout and editing via StoryBoard.
An Event View based on Apple's Event Detail View. Written in Swift 3. Supports ARC, Autolayout and editing via StoryBoard.

An Event View based on Apple's Event Detail View. Written in Swift 3. Supports ARC, Autolayout and editing via StoryBoard. Installation CocoaPods PTEv

Feb 14, 2021