master acc2e63aebd4 cached
22 files
79.7 KB
21.0k tokens
1 requests
Download .txt
Repository: joshbirnholz/JBCalendarDatePicker
Branch: master
Commit: acc2e63aebd4
Files: 22
Total size: 79.7 KB

Directory structure:
gitextract_jmv4cs2m/

├── .gitignore
├── .swiftpm/
│   └── xcode/
│       ├── package.xcworkspace/
│       │   └── contents.xcworkspacedata
│       └── xcshareddata/
│           └── xcschemes/
│               └── JBCalendarDatePicker.xcscheme
├── JBCalendarDatePicker.podspec
├── LICENSE
├── Package.swift
├── README.md
├── Sources/
│   ├── Info.plist
│   └── JBCalendarDatePicker/
│       ├── CalendarDatePickerViewController.h
│       ├── DateInputView.swift
│       ├── Day.swift
│       ├── JBCalendarDateCell.swift
│       ├── JBCalendarDateCell.xib
│       ├── JBCalendarDatePicker.h
│       ├── JBCalendarViewController.swift
│       ├── JBCalendarViewController.xib
│       ├── JBDatePicker.swift
│       ├── JBDatePickerViewController.swift
│       ├── JBDatePickerViewController.xib
│       └── UIColor+SystemAccent.swift
└── Tests/
    └── JBCalendarDatePickerTests/
        ├── JBCalendarDatePickerTests.swift
        └── XCTestManifests.swift

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/


================================================
FILE: .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:">
   </FileRef>
</Workspace>


================================================
FILE: .swiftpm/xcode/xcshareddata/xcschemes/JBCalendarDatePicker.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "1230"
   version = "1.3">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "JBCalendarDatePicker_JBCalendarDatePicker"
               BuildableName = "JBCalendarDatePicker_JBCalendarDatePicker"
               BlueprintName = "JBCalendarDatePicker_JBCalendarDatePicker"
               ReferencedContainer = "container:">
            </BuildableReference>
         </BuildActionEntry>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "JBCalendarDatePicker"
               BuildableName = "JBCalendarDatePicker"
               BlueprintName = "JBCalendarDatePicker"
               ReferencedContainer = "container:">
            </BuildableReference>
         </BuildActionEntry>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "NO"
            buildForArchiving = "NO"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "JBCalendarDatePickerTests"
               BuildableName = "JBCalendarDatePickerTests"
               BlueprintName = "JBCalendarDatePickerTests"
               ReferencedContainer = "container:">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES">
      <Testables>
         <TestableReference
            skipped = "NO">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "JBCalendarDatePickerTests"
               BuildableName = "JBCalendarDatePickerTests"
               BlueprintName = "JBCalendarDatePickerTests"
               ReferencedContainer = "container:">
            </BuildableReference>
         </TestableReference>
      </Testables>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "JBCalendarDatePicker_JBCalendarDatePicker"
            BuildableName = "JBCalendarDatePicker_JBCalendarDatePicker"
            BlueprintName = "JBCalendarDatePicker_JBCalendarDatePicker"
            ReferencedContainer = "container:">
         </BuildableReference>
      </MacroExpansion>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>


================================================
FILE: JBCalendarDatePicker.podspec
================================================
Pod::Spec.new do |s|

# 1
s.platform = :ios
s.ios.deployment_target = '13.0'
s.name = "JBCalendarDatePicker"
s.summary = "A replacement for UIDatePicker made for Catalyst."
s.requires_arc = true

# 2
s.version = "0.2.3"

# 3
s.license = { :type => "MIT", :file => "LICENSE" }

# 4 - Replace with your name and e-mail address
s.author = { "Josh Birnholz" => "josh@birnholz.com" }

# 5 - Replace this URL with your own GitHub page's URL (from the address bar)
s.homepage = "https://github.com/joshbirnholz/JBCalendarDatePicker"

# 6 - Replace this URL with your own Git URL from "Quick Setup"
s.source = { :git => "https://github.com/joshbirnholz/JBCalendarDatePicker.git",
             :tag => "#{s.version}" }

# 7
s.framework = "UIKit"

# 8
s.source_files = "JBCalendarDatePicker/**/*.{swift}"

# 9
s.resources = "JBCalendarDatePicker/**/*.{xib}"

# 10
s.swift_version = "5"

end


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Joshua Birnholz

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

================================================
FILE: Package.swift
================================================
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "JBCalendarDatePicker",
    platforms: [
        .iOS(.v13),
    ],
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "JBCalendarDatePicker",
            targets: ["JBCalendarDatePicker"]),
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "JBCalendarDatePicker",
            dependencies: [],
            path: "Sources",
            exclude: ["Info.plist"],
            resources: [
                .process("JBCalendarDatePicker/JBDatePickerViewController.xib"),
                .process("JBCalendarDatePicker/JBCalendarViewController.xib"),
                .process("JBCalendarDatePicker/JBCalendarDateCell.xib"),
            ]
        ),
        .testTarget(
            name: "JBCalendarDatePickerTests",
            dependencies: ["JBCalendarDatePicker"],
            path: "Tests"
        ),
    ],
    swiftLanguageVersions: [.v5]
)


================================================
FILE: README.md
================================================

# JBCalendarDatePicker
A replacement for UIDatePicker made for Catalyst.

This is still a work in progress, there are bugs, and although it's written to work with different calendar systems and locales, it's not guaranteed to work correctly with everything!

![JBCalendarDatePicker](https://i.imgur.com/XusV7dx.gif)

## Installation

To install as SPM, Go to:
`Xcode -> File -> Swift Packages -> Add Package Dependency`

Then enter this URL:
`https://github.com/mohitnandwani/JBCalendarDatePicker.git`

To install, add the source to the top of your podfile:

`source 'https://github.com/joshbirnholz/JBPodSpecs.git'`

Then add this pod to your targets:

`pod 'JBCalendarDatePicker'`

## Use

There are two classes you can use: `JBDatePickerViewController` and `JBCalendarViewController`.

They are both similar to `UIDatePicker`, and their `date`, `minimumDate`, `maximumDate`, `calendar`, and `locale` properties can be configured in the same way. Configure them before presenting either of the view controllers.

`JBDatePickerViewController` also has a `datePickerMode` property, although `UIDatePicker.Mode.countDownTimer` is not supported.

### JBDatePickerViewController

![JBDatePickerViewController](https://i.imgur.com/OtPr5V7.png)

`JBDatePickerViewController` displays labels showing its represented date and allows the user to use the keyboard to enter a date. When the user clicks on the date portion, the view controller presents its own `JBCalendarViewController`. You can allow the user to select a date, time, or both, by setting the `datePickerMode` property.

```Swift
import JBCalendarDatePicker

class ViewController: UIViewController {

	var datePicker: JBDatePickerViewController!
	
	override func viewDidLoad() {
		super.viewDidLoad()
		
		let datePicker = JBDatePickerViewController()
		view.addSubview(datePicker.view)
		addChild(datePicker)
		datePicker.didMove(toParent: self)
		self.datePicker = datePicker

		// Configure the datePicker's properties
	}
}
```

Or use it from a storyboard. Drag a Container View onto your storyboard. Change the view controller's class to `JBDatePickerViewController`. Give the embed segue an identifier, and then capture a reference to it:

```Swift
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
	if segue.identifier == "Embed Date Picker", let destination = segue.destination as? JBDatePickerViewController {
		self.datePicker = destination
		
		// Configure the datePicker's properties
	}
}
```

### JBCalendarViewController

![JBCalendarViewController](https://i.imgur.com/NV48jUk.png)

`JBCalendarViewController` is just the calendar, without the labels.

The view controller tries to present itself as a popover automatically, so be sure to set the `popoverPresentationController`'s `barButtonItem` property or the `sourceView` and `sourceRect` properties.

```Swift
@IBOutlet func buttonPressed(_ sender: UIBarButtonItem) {
	let calendarPicker = JBCalendarViewController()
	calendarPicker.popoverPresentationController?.barButtonItem = sender
	
	// Configure the calendar's properties
	
	present(calendarPicker, animated: true, completion: nil)
}
```
There is also a `JBCalendarViewControllerDelegate` protocol.

```Swift
public protocol JBCalendarViewControllerDelegate: class {
	func calendarViewControllerDateChanged(_ calendarViewController: JBCalendarViewController)
	func calendarViewControllerWillDismiss(_ calendarViewController: JBCalendarViewController)
	func calendarViewControllerDidDismiss(_ calendarViewController: JBCalendarViewController)
}
```


================================================
FILE: Sources/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>$(DEVELOPMENT_LANGUAGE)</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
	<key>CFBundleShortVersionString</key>
	<string>$(MARKETING_VERSION)</string>
	<key>CFBundleVersion</key>
	<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>


================================================
FILE: Sources/JBCalendarDatePicker/CalendarDatePickerViewController.h
================================================
//
//  CalendarDatePickerViewController.h
//  CalendarDatePickerViewController
//
//  Created by Josh Birnholz on 28/10/2019.
//  Copyright © 2019 Josh Birnholz. All rights reserved.
//

#import <Foundation/Foundation.h>

//! Project version number for CalendarDatePickerViewController.
FOUNDATION_EXPORT double CalendarDatePickerViewControllerVersionNumber;

//! Project version string for CalendarDatePickerViewController.
FOUNDATION_EXPORT const unsigned char CalendarDatePickerViewControllerVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <CalendarDatePickerViewController/PublicHeader.h>




================================================
FILE: Sources/JBCalendarDatePicker/DateInputView.swift
================================================
//
//  DateInputView.swift
//  CalendarDatePickerViewController
//
//  Created by Josh Birnholz on 10/29/19.
//  Copyright © 2019 Josh Birnholz. All rights reserved.
//

#if canImport(UIKit)
import UIKit

protocol DateInputViewDelegate: UIResponder, UIKeyInput {
	
}

class DateInputView: UIView, UIKeyInput {
	
	weak var delegate: DateInputViewDelegate?
	
	override func becomeFirstResponder() -> Bool {
		print(type(of: self), #function)
		let value = super.becomeFirstResponder()
		print(type(of: self), "is first responder: \(self.isFirstResponder)")
		return value
	}
	
	override func resignFirstResponder() -> Bool {
		print(type(of: self), #function)
		return delegate?.resignFirstResponder() ?? super.resignFirstResponder()
	}
	
	override var canBecomeFirstResponder: Bool {
		return true
	}
	
	// MARK: UIKeyInput
	
	var hasText: Bool {
		return delegate?.hasText ?? false
	}
	
	func insertText(_ text: String) {
		delegate?.insertText(text)
	}
	
	func deleteBackward() {
		delegate?.deleteBackward()
	}
	
	// MARK: UITextInputTraits
	
	// this doesn't seem to work for some reason.
	private var keyboardType: UIKeyboardType {
		return .numberPad
	}

}
#endif


================================================
FILE: Sources/JBCalendarDatePicker/Day.swift
================================================
//
//  Day.swift
//  CalendarDatePickerViewController
//
//  Created by Josh Birnholz on 28/10/2019.
//  Copyright © 2019 Josh Birnholz. All rights reserved.
//

import Foundation

struct Day: Equatable, Hashable {
	var calendar: Calendar
	var day: Int
	var month: Int
	var year: Int
	
	var date: Date {
		DateComponents(calendar: calendar, year: year, month: month, day: day).date!
	}
	
	var isToday: Bool {
		let todayComponents = calendar.dateComponents([.year, .month, .day], from: Date())
		var components = DateComponents(calendar: calendar, year: year, month: month, day: day)
		let date = calendar.date(from: components)!
		components = calendar.dateComponents([.year, .month, .day], from: date)
		return todayComponents.day == components.day && todayComponents.month == components.month && todayComponents.year == components.year
	}
	
	static func == (lhs: Day, rhs: Day) -> Bool {
		if lhs.day == rhs.day && lhs.month == rhs.month && lhs.year == rhs.year {
			return true
		}
		return lhs.date == rhs.date
	}
}


================================================
FILE: Sources/JBCalendarDatePicker/JBCalendarDateCell.swift
================================================
//
//  CalendarDateCollectionViewCell.swift
//  CalendarDatePickerViewController
//
//  Created by Josh Birnholz on 28/10/2019.
//  Copyright © 2019 Josh Birnholz. All rights reserved.
//

#if canImport(UIKit)
import UIKit

class JBCalendarDateCell: UICollectionViewCell {
	@IBOutlet weak var label: UILabel!
}
#endif


================================================
FILE: Sources/JBCalendarDatePicker/JBCalendarDateCell.xib
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15400" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina6_1" orientation="portrait" appearance="light"/>
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="JBCalendarDateCell" customModule="CalendarDatePickerViewController" customModuleProvider="target"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="JBCalendarDateCell" customModule="JBCalendarDatePicker">
            <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="31" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ucl-T0-oTc">
                    <rect key="frame" x="4" y="48" width="406" height="810"/>
                    <fontDescription key="fontDescription" type="system" pointSize="13"/>
                    <nil key="textColor"/>
                    <nil key="highlightedColor"/>
                </label>
            </subviews>
            <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
            <constraints>
                <constraint firstItem="Ucl-T0-oTc" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="4" id="Gb8-nW-Kyz"/>
                <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="Ucl-T0-oTc" secondAttribute="trailing" constant="4" id="aSb-bC-zIs"/>
                <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="Ucl-T0-oTc" secondAttribute="bottom" constant="4" id="gmd-5m-i2m"/>
                <constraint firstItem="Ucl-T0-oTc" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="4" id="xd2-do-aau"/>
            </constraints>
            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
            <connections>
                <outlet property="label" destination="Ucl-T0-oTc" id="iXA-dA-E4K"/>
            </connections>
            <point key="canvasLocation" x="139" y="118"/>
        </view>
    </objects>
</document>


================================================
FILE: Sources/JBCalendarDatePicker/JBCalendarDatePicker.h
================================================
//
//  JBDatePicker.h
//  JBDatePicker
//
//  Created by Josh Birnholz on 10/30/19.
//  Copyright © 2019 Josh Birnholz. All rights reserved.
//

#import <Foundation/Foundation.h>

//! Project version number for JBDatePicker.
FOUNDATION_EXPORT double JBDatePickerVersionNumber;

//! Project version string for JBDatePicker.
FOUNDATION_EXPORT const unsigned char JBDatePickerVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <JBDatePicker/PublicHeader.h>




================================================
FILE: Sources/JBCalendarDatePicker/JBCalendarViewController.swift
================================================
//
//  CalendarDatePickerViewController.swift
//  Calendar Picker
//
//  Created by Josh Birnholz on 10/27/19.
//  Copyright © 2019 Josh Birnholz. All rights reserved.
//

#if canImport(UIKit)
import UIKit

@objc public protocol JBCalendarViewControllerDelegate: class {
	func calendarViewControllerDateChanged(_ calendarViewController: JBCalendarViewController)
	func calendarViewControllerWillDismiss(_ calendarViewController: JBCalendarViewController)
	func calendarViewControllerDidDismiss(_ calendarViewController: JBCalendarViewController)
}

public class JBCalendarViewController: UIViewController, JBDatePicker {
	
	@objc public weak var delegate: JBCalendarViewControllerDelegate?

	@IBOutlet private weak var monthLabel: UILabel!
	@IBOutlet private weak var collectionView: UICollectionView!
	
	@IBOutlet private var weekSymbolLabels: [UILabel]!
	
	/// This property always returns `UIDatePicker.Mode.date`. Setting this property to a new value does nothing. It is not possible to change the date picker mode of the calendar interface.
	@objc public var datePickerMode: UIDatePicker.Mode {
		get {
			return .date
		}
		set {
			
		}
	}
	
	@objc public var calendar: Calendar! = Calendar.current {
		didSet {
			if calendar == nil {
				calendar = .current
			}
			updateWeekLabels()
		}
	}
	
	@objc public var locale: Locale? = .current {
		didSet {
			calendar.locale = locale
		}
	}
	
	@objc public var date: Date = Date() {
		didSet {
			switch (minimumDate, maximumDate) {
			case(let minimumDate?, let maximumDate?) where minimumDate < maximumDate :
				date = min(max(date, minimumDate), maximumDate)
			case (let minimumDate?, nil):
				date = max(date, minimumDate)
			case (nil, let maximumDate?):
				date = min(date, maximumDate)
			default:
				break
			}
			
			if current != nil {
				let components = calendar.dateComponents([.month, .year], from: date)
				
				if  components.month! != current.month || components.year! != current.year {
					(current.month, current.year) = (components.month!, components.year!)
				} else {
					collectionView?.reloadData()
				}
			}
			
			delegate?.calendarViewControllerDateChanged(self)
		}
	}
	
	@objc public var minimumDate: Date? {
		didSet {
			collectionView?.reloadData()
		}
	}
	@objc public var maximumDate: Date? {
		didSet {
			collectionView?.reloadData()
		}
	}
	
	private var usableMinimumDate: Date? {
		if let minimumDate = minimumDate {
			if let maximumDate = maximumDate {
				if minimumDate < maximumDate {
					return minimumDate
				} else {
					return nil
				}
			}
			return minimumDate
		}
		
		return nil
	}
	
	private var usableMaximumDate: Date? {
		if let maximumDate = maximumDate {
			if let minimumDate = minimumDate {
				if minimumDate < maximumDate {
					return maximumDate
				} else {
					return nil
				}
			}
			return maximumDate
		}
		
		return nil
	}
	
	private var selectedDay: Day {
		let components = calendar.dateComponents([.day, .month, .year], from: date)
		return Day(calendar: calendar, day: components.day!, month: components.month!, year: components.year!)
	}
	
	private struct Current {
		var month: Int {
			didSet {
				let firstOfMonth = DateComponents(calendar: calendar, year: year, month: month, day: 1).date!
				let range = calendar.range(of: .month, in: .year, for: firstOfMonth)!
				if month > range.last! {
					month = range.first!
					year += 1
				} else if month < range.first! {
					month = range.last!
					year -= 1
				}
			}
		}
		
		var year: Int
		
		private let calendar: Calendar

		init(calendar: Calendar, month: Int, year: Int) {
			self.calendar = calendar
			self.month = month
			self.year = year
		}
		
		
	}
	
	private var current: Current! {
		didSet {
			updateMonthLabel()
			updateDays()
			collectionView.reloadData()
		}
	}
	
	private var days: [Day] = []
	
	public required init?(coder: NSCoder) {
		super.init(nibName: "JBCalendarViewController", bundle: Bundle(for: Self.self))
		commonInit()
	}
	
	public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
		super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
		commonInit()
	}
	
	public init() {
		super.init(nibName: "JBCalendarViewController", bundle: Bundle(for: Self.self))
		commonInit()
	}
	
	private func commonInit() {
		modalPresentationStyle = .popover
		popoverPresentationController?.delegate = self
		preferredContentSize = CGSize(width: 200, height: 210)
	}
	
	override public func viewDidLoad() {
        super.viewDidLoad()
		
		calendar.locale = self.locale
		
		collectionView.delegate = self
		collectionView.dataSource = self
		collectionView.register(UINib(nibName: "JBCalendarDateCell", bundle: Bundle(for: Self.self)), forCellWithReuseIdentifier: "DateCell")
		
		#if targetEnvironment(macCatalyst)
		view.tintColor = .systemAccent
		#endif
		
		let selectedComponents = calendar.dateComponents([.year, .month], from: date)
		current = Current(calendar: calendar, month: selectedComponents.month!, year: selectedComponents.year!)
		
		let pan = UIPanGestureRecognizer(target: self, action: #selector(didPan(toSelectCells:)))
		collectionView.addGestureRecognizer(pan)
		
		let prevLong = UILongPressGestureRecognizer(target: self, action: #selector(previousMonthButtonTouchDown(_:)))
		prevLong.minimumPressDuration = 0
    
	}
	
	private func updateWeekLabels() {
		var symbols = calendar.veryShortStandaloneWeekdaySymbols
		if (calendar.locale ?? .current).languageCode == "en" {
			symbols = calendar.shortStandaloneWeekdaySymbols.map { String($0.prefix(2)) }
		}
		guard isViewLoaded else { return }
		for (index, symbol) in symbols.enumerated() {
			weekSymbolLabels[index].text = symbol
		}
	}
	
	private func updateDays() {
		let components = DateComponents(calendar: calendar, year: current.year, month: current.month)
		let date = components.date!
		
		let range = calendar.range(of: .day, in: .month, for: date)!
		
		days = range.map { Day(calendar: calendar, day: $0, month: current.month, year: current.year) }
		
		let startDate = calendar.dateInterval(of: .month, for: date)!.start
		let weekday = calendar.component(.weekday, from: startDate)
		
		let firstDay = days.first!
		for i in 0 ..< weekday-1 {
			var day = firstDay
			day.day -= i+1
			days.insert(day, at: 0)
		}
		
		let lastDay = days.last!
		let count = calendar.weekdaySymbols.count * 6
		for i in 0 ..< (count-days.count) {
			var day = lastDay
			day.day += i+1
			days.append(day)
		}
	}
	
	private func updateMonthLabel() {
		guard isViewLoaded else { return }
		
		let formatter = DateFormatter()
		formatter.locale = calendar.locale
		formatter.setLocalizedDateFormatFromTemplate("MMM yyyy")
		let components = DateComponents(calendar: calendar, year: current.year, month: current.month)
		monthLabel.text = formatter.string(from: components.date!)
	}
	
	@IBAction private func previousMonthButtonTouchUp(_ sender: Any) {
		timer?.invalidate()
		timer = nil
	}
	
	@IBAction private func selectedDayButtonPressed(_ sender: Any) {
		let components = calendar.dateComponents([.month, .year], from: date)
		let month = components.month!
		let year = components.year!
		current = Current(calendar: calendar, month: month, year: year)
	}
	
	@IBAction private func nextMonthButtonTouchUp(_ sender: Any) {
		timer?.invalidate()
		timer = nil
	}
	
	@IBAction private func previousMonthButtonTouchDown(_ sender: Any) {
		startRepeatingTimer { [weak self] in
			self?.current.month -= 1
		}
	}
	
	private var timer: Timer?
	private func startRepeatingTimer(_ action: @escaping () -> Void) {
		action()
		
		timer = Timer(fire: Date().addingTimeInterval(0.5), interval: 0.25, repeats: true) { timer in
			action()
		}
		RunLoop.main.add(timer!, forMode: .common)
		
		DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
			if let timer = self.timer {
				timer.invalidate()
				self.timer = Timer(timeInterval: 0.075, repeats: true, block: { timer in
					action()
				})
				RunLoop.main.add(self.timer!, forMode: .common)
			}
		}
	}
	
	@IBAction private func nextMonthButtonTouchDown(_ sender: Any) {
		startRepeatingTimer { [weak self] in
			self?.current.month += 1
		}
	}
		
	private var lastPanChangeDate: Date = Date()
	@objc private func didPan(toSelectCells panGesture: UIPanGestureRecognizer) {
		if panGesture.state == .began {
			collectionView.isUserInteractionEnabled = false
		} else if panGesture.state == .changed, let indexPath = collectionView.indexPathForItem(at: panGesture.location(in: collectionView)) {
			let day = days[indexPath.row]
			let date = DateComponents(calendar: calendar, year: current.year, month: current.month, day: day.day).date!
			let month = calendar.component(.month, from: date)
			
			if month == current.month || -lastPanChangeDate.timeIntervalSinceNow > 0.8 {
				self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: [])
				self.collectionView(collectionView, didSelectItemAt: indexPath)
				
				lastPanChangeDate = Date()
			}
		} else if panGesture.state == .ended {
			collectionView.isUserInteractionEnabled = true
		}
	}
	
}

extension JBCalendarViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
	public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
		return days.count
	}
	
	public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
			let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DateCell", for: indexPath) as! JBCalendarDateCell
		let day = days[indexPath.row]
		
		let date = DateComponents(calendar: calendar, year: current.year, month: current.month, day: day.day).date!
		let components = calendar.dateComponents([.day, .month], from: date)
			
		cell.label.text = String(components.day!)
		cell.layer.cornerRadius = 4
		cell.layer.masksToBounds = true
		
		let isSelected = day == selectedDay
		
		let highlightedBackgroundColor: UIColor = day.isToday ? view.tintColor : .systemFill
		cell.backgroundColor = isSelected ? highlightedBackgroundColor : nil
		
		if day.isToday {
			if isSelected {
				cell.label.textColor = .lightLabel
			} else {
				cell.label.textColor = self.view.tintColor
			}
		} else if let minimumDate = usableMinimumDate, date < minimumDate {
			cell.label.textColor = .quaternaryLabel
		} else if let maximumDate = usableMaximumDate, date > maximumDate {
			cell.label.textColor = .quaternaryLabel
		} else if components.month == current.month {
			cell.label.textColor = .label
		} else {
			cell.label.textColor = .tertiaryLabel
		}
		
		return cell
	}
	
	public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
		
		let numberOfItems = collectionView.numberOfItems(inSection: 0)
		
		let spacing = (collectionViewLayout as! UICollectionViewFlowLayout).minimumInteritemSpacing * CGFloat(numberOfItems-1)
		let width = (self.collectionView.frame.width - spacing) / CGFloat(calendar.weekdaySymbols.count)
		let height = (self.collectionView.frame.height - spacing) / CGFloat(6)
		
		return CGSize(width: width, height: height)
	}
	
	public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
		let day = days[indexPath.row]
		
		var components = calendar.dateComponents([.timeZone, .year, .month, .day, .hour, .minute, .second, .nanosecond], from: self.date)
		components.day = day.day
		components.month = day.month
		components.year = day.year
		let newDate = calendar.date(from: components)!
		
		if let minimumDate = usableMinimumDate, newDate < minimumDate {
			return
		} else if let maximumDate = usableMaximumDate, newDate > maximumDate {
			return
		}
		
		collectionView.reloadData()
		
		self.date = newDate
	}
	
	public func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
		collectionView.reloadData()
	}
	
}

extension JBCalendarViewController: UIPopoverPresentationControllerDelegate {
	public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
		return .none
	}
	
	public func presentationControllerWillDismiss(_ presentationController: UIPresentationController) {
		delegate?.calendarViewControllerWillDismiss(self)
		print("final date:", date)
	}
	
	public func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
		delegate?.calendarViewControllerDidDismiss(self)
	}
}
#endif


================================================
FILE: Sources/JBCalendarDatePicker/JBCalendarViewController.xib
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15400" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina6_1" orientation="portrait" appearance="light"/>
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="JBCalendarViewController" customModule="JBCalendarDatePicker">
            <connections>
                <outlet property="collectionView" destination="SJT-dF-4Dv" id="Fgp-sQ-slW"/>
                <outlet property="monthLabel" destination="XsS-gG-JOn" id="t3u-EQ-qBq"/>
                <outlet property="view" destination="iN0-l3-epB" id="H4Q-QN-OzQ"/>
                <outletCollection property="weekSymbolLabels" destination="NzH-cg-g4f" collectionClass="NSMutableArray" id="PvR-50-ZTa"/>
                <outletCollection property="weekSymbolLabels" destination="Jh7-Xy-ne4" collectionClass="NSMutableArray" id="Adw-v9-yyb"/>
                <outletCollection property="weekSymbolLabels" destination="GHT-nG-QP9" collectionClass="NSMutableArray" id="WCv-rU-ebN"/>
                <outletCollection property="weekSymbolLabels" destination="BZX-9S-jZP" collectionClass="NSMutableArray" id="aFo-et-a8d"/>
                <outletCollection property="weekSymbolLabels" destination="nN9-YF-bv0" collectionClass="NSMutableArray" id="kyZ-tY-HYN"/>
                <outletCollection property="weekSymbolLabels" destination="Ecz-Oh-w0S" collectionClass="NSMutableArray" id="sEL-kp-Kwx"/>
                <outletCollection property="weekSymbolLabels" destination="ju0-r4-MOj" collectionClass="NSMutableArray" id="zrb-Xp-dvd"/>
            </connections>
        </placeholder>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="iN0-l3-epB">
            <rect key="frame" x="0.0" y="0.0" width="200" height="220"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="1" translatesAutoresizingMaskIntoConstraints="NO" id="9zH-yi-fyW">
                    <rect key="frame" x="8" y="52" width="184" height="160"/>
                    <subviews>
                        <stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="P47-Dg-Btf">
                            <rect key="frame" x="0.0" y="0.0" width="184" height="28"/>
                            <subviews>
                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="249" verticalHuggingPriority="251" text="Oct 2019" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XsS-gG-JOn">
                                    <rect key="frame" x="0.0" y="0.0" width="116" height="28"/>
                                    <fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>
                                    <nil key="textColor"/>
                                    <nil key="highlightedColor"/>
                                </label>
                                <stackView opaque="NO" contentMode="scaleToFill" spacing="-11" translatesAutoresizingMaskIntoConstraints="NO" id="L4a-pK-Onm">
                                    <rect key="frame" x="116" y="0.0" width="68" height="28"/>
                                    <subviews>
                                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5ZB-PE-x1r">
                                            <rect key="frame" x="0.0" y="0.0" width="30" height="28"/>
                                            <fontDescription key="fontDescription" type="system" pointSize="13"/>
                                            <color key="tintColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
                                            <state key="normal" title="◀︎"/>
                                            <connections>
                                                <action selector="previousMonthButtonTouchDown:" destination="-1" eventType="touchDown" id="n3F-ID-pNe"/>
                                                <action selector="previousMonthButtonTouchUp:" destination="-1" eventType="touchUpOutside" id="4Cm-wu-jUl"/>
                                                <action selector="previousMonthButtonTouchUp:" destination="-1" eventType="touchUpInside" id="KIQ-1V-UtW"/>
                                            </connections>
                                        </button>
                                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PXw-Xa-rec">
                                            <rect key="frame" x="19" y="0.0" width="30" height="28"/>
                                            <fontDescription key="fontDescription" type="system" pointSize="11"/>
                                            <color key="tintColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
                                            <state key="normal" title="●"/>
                                            <connections>
                                                <action selector="selectedDayButtonPressed:" destination="-1" eventType="touchUpInside" id="NeK-ty-u1o"/>
                                            </connections>
                                        </button>
                                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nba-2C-EMV">
                                            <rect key="frame" x="38" y="0.0" width="30" height="28"/>
                                            <fontDescription key="fontDescription" type="system" pointSize="13"/>
                                            <color key="tintColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
                                            <state key="normal" title="▶︎"/>
                                            <connections>
                                                <action selector="nextMonthButtonTouchDown:" destination="-1" eventType="touchDown" id="zim-hz-d73"/>
                                                <action selector="nextMonthButtonTouchUp:" destination="-1" eventType="touchUpOutside" id="Zcj-e5-rkA"/>
                                                <action selector="nextMonthButtonTouchUp:" destination="-1" eventType="touchUpInside" id="g5l-xx-0Ew"/>
                                            </connections>
                                        </button>
                                    </subviews>
                                </stackView>
                            </subviews>
                        </stackView>
                        <stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="2" translatesAutoresizingMaskIntoConstraints="NO" id="6sq-CK-ND2">
                            <rect key="frame" x="0.0" y="29" width="184" height="16"/>
                            <subviews>
                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Su" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.10000000149011612" translatesAutoresizingMaskIntoConstraints="NO" id="NzH-cg-g4f">
                                    <rect key="frame" x="0.0" y="0.0" width="24.5" height="16"/>
                                    <fontDescription key="fontDescription" type="boldSystem" pointSize="13"/>
                                    <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
                                    <nil key="highlightedColor"/>
                                </label>
                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Mo" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.10000000149011612" translatesAutoresizingMaskIntoConstraints="NO" id="Jh7-Xy-ne4">
                                    <rect key="frame" x="26.5" y="0.0" width="24.5" height="16"/>
                                    <fontDescription key="fontDescription" type="boldSystem" pointSize="13"/>
                                    <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
                                    <nil key="highlightedColor"/>
                                </label>
                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Tu" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.10000000149011612" translatesAutoresizingMaskIntoConstraints="NO" id="GHT-nG-QP9">
                                    <rect key="frame" x="53" y="0.0" width="24.5" height="16"/>
                                    <fontDescription key="fontDescription" type="boldSystem" pointSize="13"/>
                                    <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
                                    <nil key="highlightedColor"/>
                                </label>
                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="We" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.10000000149011612" translatesAutoresizingMaskIntoConstraints="NO" id="BZX-9S-jZP">
                                    <rect key="frame" x="79.5" y="0.0" width="25" height="16"/>
                                    <fontDescription key="fontDescription" type="boldSystem" pointSize="13"/>
                                    <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
                                    <nil key="highlightedColor"/>
                                </label>
                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Th" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.10000000149011612" translatesAutoresizingMaskIntoConstraints="NO" id="nN9-YF-bv0">
                                    <rect key="frame" x="106.5" y="0.0" width="24.5" height="16"/>
                                    <fontDescription key="fontDescription" type="boldSystem" pointSize="13"/>
                                    <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
                                    <nil key="highlightedColor"/>
                                </label>
                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Fr" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.10000000149011612" translatesAutoresizingMaskIntoConstraints="NO" id="Ecz-Oh-w0S">
                                    <rect key="frame" x="133" y="0.0" width="24.5" height="16"/>
                                    <fontDescription key="fontDescription" type="boldSystem" pointSize="13"/>
                                    <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
                                    <nil key="highlightedColor"/>
                                </label>
                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sa" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.10000000149011612" translatesAutoresizingMaskIntoConstraints="NO" id="ju0-r4-MOj">
                                    <rect key="frame" x="159.5" y="0.0" width="24.5" height="16"/>
                                    <fontDescription key="fontDescription" type="boldSystem" pointSize="13"/>
                                    <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
                                    <nil key="highlightedColor"/>
                                </label>
                            </subviews>
                        </stackView>
                        <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="eop-u5-hdg">
                            <rect key="frame" x="0.0" y="46" width="184" height="4.5"/>
                            <subviews>
                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8vZ-od-Ehu">
                                    <rect key="frame" x="0.0" y="0.0" width="184" height="3"/>
                                    <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
                                    <constraints>
                                        <constraint firstAttribute="height" constant="3" id="duy-FI-4zu"/>
                                    </constraints>
                                </view>
                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5YM-lx-cg4">
                                    <rect key="frame" x="0.0" y="3" width="184" height="1.5"/>
                                    <color key="backgroundColor" systemColor="quaternaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.17999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
                                    <constraints>
                                        <constraint firstAttribute="height" constant="1.3" id="LYl-xp-ZRp"/>
                                    </constraints>
                                </view>
                            </subviews>
                        </stackView>
                        <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" prefetchingEnabled="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SJT-dF-4Dv">
                            <rect key="frame" x="0.0" y="51.5" width="184" height="108.5"/>
                            <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
                            <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="JWd-Xu-Nj4">
                                <size key="itemSize" width="26" height="24"/>
                                <size key="headerReferenceSize" width="0.0" height="0.0"/>
                                <size key="footerReferenceSize" width="0.0" height="0.0"/>
                                <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
                            </collectionViewFlowLayout>
                            <cells/>
                        </collectionView>
                    </subviews>
                </stackView>
            </subviews>
            <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
            <constraints>
                <constraint firstItem="9zH-yi-fyW" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="8" id="FoA-L7-RMt"/>
                <constraint firstItem="9zH-yi-fyW" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="8" id="QPv-6S-a73"/>
                <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="9zH-yi-fyW" secondAttribute="bottom" constant="8" id="Rbt-yF-kR7"/>
                <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="9zH-yi-fyW" secondAttribute="trailing" constant="8" id="dsb-tS-kE2"/>
            </constraints>
            <nil key="simulatedTopBarMetrics"/>
            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
            <point key="canvasLocation" x="139" y="130"/>
        </view>
    </objects>
</document>


================================================
FILE: Sources/JBCalendarDatePicker/JBDatePicker.swift
================================================
//
//  JBDatePicker.swift
//  CalendarDatePickerViewController
//
//  Created by Josh Birnholz on 10/29/19.
//  Copyright © 2019 Josh Birnholz. All rights reserved.
//

#if canImport(UIKit)
import UIKit


public protocol JBDatePicker: UIResponder {
	var date: Date { get set }
	var calendar: Calendar! { get set }
	var locale: Locale? { get set }
	var minimumDate: Date? { get set }
	var maximumDate: Date? { get set }
	var datePickerMode: UIDatePicker.Mode { get set }
}
#endif


================================================
FILE: Sources/JBCalendarDatePicker/JBDatePickerViewController.swift
================================================
//
//  JBDatePickerViewController.swift
//  CalendarDatePickerViewController
//
//  Created by Josh Birnholz on 28/10/2019.
//  Copyright © 2019 Josh Birnholz. All rights reserved.
//

#if canImport(UIKit)
import UIKit

public class JBDatePickerViewController: UIViewController, DateInputViewDelegate, JBDatePicker {
	
	// MARK: Public interface
	
	private var keyboardType: UIKeyboardType {
		return .numberPad
	}
	
	/// Use this property to change the type of information displayed by the date picker. It determines whether the date picker allows selection of a date, a time, or both date and time. The default mode is `UIDatePicker.Mode.dateAndTime`. See `UIDatePicker.Mode` for a list of mode constants.
	///
	/// Setting this property to `UIDatePicker.Mode.countDownTimer` has no effect; this date picker does not support the countdown timer mode.
	@objc public var datePickerMode: UIDatePicker.Mode = .dateAndTime {
		didSet {
			if datePickerMode == .countDownTimer {
				datePickerMode = oldValue
			}
		}
	}
	
	private var dateInputView: DateInputView! {
		return (view as! DateInputView)
	}
	
	public var calendar: Calendar! = Calendar.current {
		didSet {
			if calendar == nil {
				calendar = .current
			}
		}
	}
	
	@objc public var locale: Locale? = .current {
		didSet {
			calendar.locale = locale
		}
	}
	
	@objc public var date: Date = Date() {
		didSet {
			switch (minimumDate, maximumDate) {
			case(let minimumDate?, let maximumDate?) where minimumDate < maximumDate :
				date = min(max(date, minimumDate), maximumDate)
			case (let minimumDate?, nil):
				date = max(date, minimumDate)
			case (nil, let maximumDate?):
				date = min(date, maximumDate)
			default:
				break
			}
			updateLabelText()
			setTextInputString("", updatingLabel: false)
			print("date set to \(date)")
			isPM = (12...23).contains(calendar.component(.hour, from: date))
			
			presentedCalendar?.delegate = nil
			presentedCalendar?.date = date
			presentedCalendar?.delegate = self
		}
	}
	
	@objc public var minimumDate: Date? {
		didSet {
			updateLabelText()
		}
	}
	@objc public var maximumDate: Date? {
		didSet {
			updateLabelText()
		}
	}
	
	private var usableMinimumDate: Date? {
		if let minimumDate = minimumDate {
			if let maximumDate = maximumDate {
				if minimumDate < maximumDate {
					return minimumDate
				} else {
					return nil
				}
			}
			return minimumDate
		}
		
		return nil
	}
	
	private var usableMaximumDate: Date? {
		if let maximumDate = maximumDate {
			if let minimumDate = minimumDate {
				if minimumDate < maximumDate {
					return maximumDate
				} else {
					return nil
				}
			}
			return maximumDate
		}
		
		return nil
	}
	
	fileprivate var _textInputString: String = ""
	fileprivate var textInputString: String { return _textInputString }
	
	fileprivate func setTextInputString(_ newValue: String, updatingLabel: Bool) {
		_textInputString = newValue
		if updatingLabel, let selectedDatePart = selectedDatePart {
			label(for: selectedDatePart).text = _textInputString
		}
	}
	
	// MARK: Init
	
	public required init?(coder: NSCoder) {
		super.init(nibName: "JBDatePickerViewController", bundle: Bundle(for: Self.self))
		commonInit()
	}
	
	public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
		super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
		commonInit()
	}
	
	public init() {
		super.init(nibName: "JBDatePickerViewController", bundle: Bundle(for: Self.self))
		commonInit()
	}
	
	private func commonInit() {
		
	}
	
	@IBOutlet private var labels: [UILabel]!
	@IBOutlet private var slashLabels: [UILabel]!
	@IBOutlet private weak var fullStackView: UIStackView!
	@IBOutlet private weak var datePartsStackView: UIStackView!
	@IBOutlet private weak var timePartsStackView: UIStackView!
	
	override public func viewDidLoad() {
        super.viewDidLoad()
		calendar.locale = locale ?? .current
		
		view.backgroundColor = .clear
		dateInputView.delegate = self
		
		#if targetEnvironment(macCatalyst)
		view.tintColor = .systemAccent
		#endif
		
		isPM = (12...23).contains(calendar.component(.hour, from: date))
		
		datePartsStackView.isHidden = datePickerMode == .time
		timePartsStackView.isHidden = datePickerMode == .date
		
		setupTextFields()
		
		updateLabelText()
		
	}
	
	override public var canBecomeFirstResponder: Bool {
		return dateInputView.canBecomeFirstResponder
	}
	
	override public func becomeFirstResponder() -> Bool {
		print(type(of: self), #function)
		if selectedDatePart == nil {
			selectedDatePart = dateParts.first
		}
		return dateInputView.becomeFirstResponder()
	}
	
	override public func resignFirstResponder() -> Bool {
		print(type(of: self), #function)
		selectedDatePart = nil
		
//		if let presented = presentedCalendar {
//			dismiss(animated: true, completion: nil)
//		}
		
		return super.resignFirstResponder()
	}
	
	@objc private func tapGestureRecognized(_ sender: UITapGestureRecognizer) {
		guard let label = sender.view as? UILabel, let datePart = self.datePart(for: label) else { return }
		selectedDatePart = datePart
		_ = dateInputView.becomeFirstResponder()
	}
	
	private var isPM = false
	
	private enum DatePart: String, CaseIterable {
		case day = "dd"
		case month = "MM"
		case year = "yyyy"
		case hour12 = "h"
		case hour24 = "HH"
		case minute = "mm"
		case amPM = "a"
		
		func set(value: Int, of components: inout DateComponents, using calendar: Calendar, isPM: Bool) {
			switch self {
			case .day:
				components.setValue(value, for: .day)
			case .month:
				// TODO: Set day to last day of month when the date range for the new month doesn't include the old day.
				components.setValue(value, for: .month)
			case .year:
				components.setValue(value, for: .year)
			case .hour12:
				var value = value
				
				if value == 12 && !isPM {
					value = 0
				} else if (1...11).contains(value) && isPM {
					value += 12
				}
				
				components.setValue(value, for: .hour)
			case .hour24:
				components.setValue(value, for: .hour)
			case .minute:
				components.setValue(value, for: .minute)
			case .amPM:
				break
			}
		}
		
		func maxComponentLength(using calendar: Calendar) -> Int {
			if self == .amPM {
				return max(calendar.amSymbol.count, calendar.pmSymbol.count)
			} else if self == .hour12 {
				return 2
			}
			
			return rawValue.count
		}
	}
	
	private var presentedCalendar: JBCalendarViewController? {
		return presentedViewController as? JBCalendarViewController
	}
	
	private var selectedDatePart: DatePart? {
		didSet {
			for datePart in visibleDateParts {
				let label = self.label(for: datePart)
				let isSelected = selectedDatePart == datePart
				label.backgroundColor = isSelected ? view.tintColor : nil
				label.textColor = isSelected ? .lightLabel : .label
			}
			
			self.setTextInputString("", updatingLabel: false)
			
			guard let selectedDatePart = selectedDatePart else {
//				presentedCalendar?.dismiss(animated: true, completion: nil)
				return
			}
			
			if selectedDatePart == .day || selectedDatePart == .month || selectedDatePart == .year && presentedCalendar == nil {
				let calendarVC = JBCalendarViewController()
				calendarVC.date = date
				calendarVC.calendar = calendar
				calendarVC.locale = locale
				calendarVC.minimumDate = minimumDate
				calendarVC.maximumDate = maximumDate
				calendarVC.popoverPresentationController?.sourceView = datePartsStackView
				calendarVC.popoverPresentationController?.sourceRect = datePartsStackView.frame
//				calendarVC.popoverPresentationController?.sourceRect = dayLabel.frame
				calendarVC.popoverPresentationController?.permittedArrowDirections = [.up]
				calendarVC.popoverPresentationController?.passthroughViews = [fullStackView]
				calendarVC.delegate = self
				self.present(calendarVC, animated: true, completion: nil)
			} else {
//				presentedCalendar?.dismiss(animated: true, completion: nil)
			}
		}
	}
	
	private var dateParts: [DatePart]! {
		didSet {
			amPMLabel.isHidden = !dateParts.contains(.hour12)
		}
	}
	
	private var yearLabel: UILabel {
		let index = dateParts.firstIndex(of: .year)!
		return labels[index]
	}
	
	private var monthLabel: UILabel {
		let index = dateParts.firstIndex(of: .month)!
		return labels[index]
	}
	
	private var dayLabel: UILabel {
		let index = dateParts.firstIndex(of: .day)!
		return labels[index]
	}
	
	@IBOutlet private weak var hourLabel: UILabel!
	@IBOutlet private weak var minuteLabel: UILabel!
	@IBOutlet private weak var amPMLabel: UILabel!
	
	private func setupTextFields() {
		var allLabels = labels ?? []
		allLabels.append(hourLabel)
		allLabels.append(minuteLabel)
		allLabels.append(amPMLabel)
		for label in allLabels {
			label.font = UIFont.monospacedDigitSystemFont(ofSize: label.font!.pointSize, weight: .regular)
			label.sizeToFit()
			NSLayoutConstraint(item: label, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: label.frame.size.width).isActive = true
			
			label.layer.masksToBounds = true
			label.layer.cornerRadius = 4
			label.backgroundColor = .clear
			label.textColor = .label
			
			let gesture = UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognized(_:)))
			label.addGestureRecognizer(gesture)
			label.isUserInteractionEnabled = true
		}
		
		let template = dateTemplate
		let dateFormat = DateFormatter.dateFormat(fromTemplate: template, options: 0, locale: locale ?? .current)!
		let components = dateFormat.split(maxSplits: .max, omittingEmptySubsequences: true, whereSeparator: { character -> Bool in
			!template.contains(character.lowercased())
		})
		dateParts = components.compactMap { DatePart(rawValue: String($0)) }
	}
	
	private let dateTemplate: String = "MMddyyyyhmma"
	
	private let formatter = DateFormatter()
	
	private var visibleDateParts: [DatePart] {
		let allowedDateParts: Set<DatePart> = {
			switch datePickerMode {
			case .time:
				if dateParts.contains(.hour12) {
					return [.hour12, .minute, .amPM]
				} else {
					return [.hour24, .minute]
				}
			case .date:
				return [.year, .month, .day]
			default:
				var returnValue: Set<DatePart> = [.year, .month, .day]
				if dateParts.contains(.hour12) {
					returnValue.insert(.hour12)
					returnValue.insert(.minute)
					returnValue.insert(.amPM)
				} else {
					returnValue.insert(.hour24)
					returnValue.insert(.minute)
				}
				return returnValue
			}
		}()
		
		return dateParts.filter { allowedDateParts.contains($0) }
	}
	
//	private var labelsAndDateParts: [(UILabel, DatePart)] {
//		return visibleDateParts.map { (self.label(for: $0), $0) }
//	}
	
	private func label(for datePart: DatePart) -> UILabel {
		switch datePart {
		case .day: return dayLabel
		case .month: return monthLabel
		case .year: return yearLabel
		case .hour12: return hourLabel
		case .hour24: return hourLabel
		case .minute: return minuteLabel
		case .amPM: return amPMLabel
		}
	}
	
	private func datePart(for label: UILabel) -> DatePart? {
		switch label {
		case yearLabel: return .year
		case monthLabel: return .month
		case dayLabel: return .day
		case hourLabel:
			if dateParts.contains(.hour12) {
				return .hour12
			}
			return .hour24
		case minuteLabel: return .minute
		case amPMLabel: return .amPM
		default: return nil
		}
	}
	
	private func updateLabelText() {
		for datePart in visibleDateParts {
			formatter.dateFormat = datePart.rawValue
			label(for: datePart).text = String(formatter.string(from: date).prefix(datePart.maxComponentLength(using: calendar)))
		}
		
		formatter.dateFormat = "a"
		amPMLabel.text = formatter.string(from: date)
	}
	
	private var finalizeEditTimer: Timer? {
		didSet {
			oldValue?.invalidate()
		}
	}

}

extension JBDatePickerViewController: UIKeyInput {
	public var hasText: Bool {
		return !textInputString.isEmpty
	}
	
	fileprivate func selectNextDatePart() {
		guard let selectedDatePart = selectedDatePart else { return }
		if let index = visibleDateParts.lastIndex(of: selectedDatePart), visibleDateParts.indices.contains(index+1) {
			self.selectedDatePart = visibleDateParts[index+1]
		} else {
			self.selectedDatePart = visibleDateParts.first
		}
	}
	
	public func insertText(_ text: String) {
		guard let selectedDatePart = selectedDatePart else { return }
		
		if text == "\t" {
			finalize(datePart: selectedDatePart)
			
			selectNextDatePart()
			
			return
		}
		
		if selectedDatePart == .amPM {
			setTextInputString(text, updatingLabel: true)
			finalize(datePart: selectedDatePart)
			return
		}
		
		guard let proposedValue = Int(textInputString + text) else { return }
		
		let validValues: [Int] = {
			switch selectedDatePart {
			case .day:
				return calendar.range(of: .day, in: .month, for: date).map(Array.init) ?? []
			case .month:
				return calendar.range(of: .month, in: .year, for: date).map(Array.init) ?? []
			case .year:
				return Array(1...9999)
			case .hour12:
				return Array(1...12)
			case .hour24:
				return Array(0...23)
			case .minute:
				return Array(0...59)
			case .amPM:
				return []
			}
		}()
		
		let valueIsValid: Bool = {
			return validValues.contains(proposedValue)
		}()
		
		guard valueIsValid else { return }
		
		setTextInputString(String(proposedValue), updatingLabel: true)
		
		if textInputString.count >= selectedDatePart.maxComponentLength(using: calendar) {
			finalize(datePart: selectedDatePart)
		} else {
			startFinalizeTimer(datePart: selectedDatePart)
		}
		
	}
	
	public func deleteBackward() {
		
		guard !textInputString.isEmpty else { return }
		var input = textInputString
		input.removeLast()
		setTextInputString(input, updatingLabel: true)
		
		if let selectedDatePart = selectedDatePart, !textInputString.isEmpty {
			startFinalizeTimer(datePart: selectedDatePart)
		}
	}
	
	private func startFinalizeTimer(datePart: DatePart) {
		finalizeEditTimer = Timer(timeInterval: 1, repeats: false) { timer in
			self.finalize(datePart: datePart)
		}
		RunLoop.main.add(finalizeEditTimer!, forMode: .common)
	}
	
	private func finalize(datePart: DatePart) {
		var components = calendar.dateComponents([.timeZone, .year, .month, .day, .hour, .minute, .second, .nanosecond], from: date)
		if let value = Int(textInputString) {
			datePart.set(value: value, of: &components, using: calendar, isPM: isPM)
			if let date = calendar.date(from: components) {
				self.date = date
			} else {
				updateLabelText()
			}
		} else if datePart == .amPM && !textInputString.isEmpty {
			if calendar.amSymbol.lowercased().hasPrefix(textInputString.lowercased()) && isPM {
				isPM = false
				print("setting to am")
				components.hour! -= 12
			} else if calendar.pmSymbol.lowercased().hasPrefix(textInputString.lowercased()) && !isPM {
				isPM = true
				print("setting to pm")
				components.hour! += 12
			}
			if let date = calendar.date(from: components) {
				self.date = date
			} else {
				updateLabelText()
			}
		}
		setTextInputString("", updatingLabel: false)
		finalizeEditTimer?.invalidate()
	}

}

extension JBDatePickerViewController: JBCalendarViewControllerDelegate {
	public func calendarViewControllerDateChanged(_ calendarViewController: JBCalendarViewController) {
		self.date = calendarViewController.date
	}
	
	public func calendarViewControllerWillDismiss(_ calendarViewController: JBCalendarViewController) {
		_ = dateInputView.resignFirstResponder()
	}
	
	public func calendarViewControllerDidDismiss(_ calendarViewController: JBCalendarViewController) {
		
	}
}

#endif


================================================
FILE: Sources/JBCalendarDatePicker/JBDatePickerViewController.xib
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15400" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina6_1" orientation="portrait" appearance="light"/>
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="JBDatePickerViewController" customModule="JBCalendarDatePicker">
            <connections>
                <outlet property="amPMLabel" destination="Yb6-cX-Iup" id="hi9-tq-aBn"/>
                <outlet property="datePartsStackView" destination="1tw-Dr-MZK" id="TQ1-js-BQA"/>
                <outlet property="fullStackView" destination="1DW-sK-Kuh" id="swT-4n-yPM"/>
                <outlet property="hourLabel" destination="wWz-nR-eEw" id="tlX-VS-R54"/>
                <outlet property="minuteLabel" destination="xBH-eh-Kkk" id="ZMz-FT-Biw"/>
                <outlet property="timePartsStackView" destination="sGC-mG-oKf" id="WOe-CT-8FY"/>
                <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
                <outletCollection property="labels" destination="e10-lT-IGg" collectionClass="NSMutableArray" id="Len-tI-HR5"/>
                <outletCollection property="slashLabels" destination="mth-vw-Bki" collectionClass="NSMutableArray" id="A1H-gr-R5g"/>
                <outletCollection property="labels" destination="CZQ-vO-Hfs" collectionClass="NSMutableArray" id="ZTh-Hs-4aQ"/>
                <outletCollection property="slashLabels" destination="4Ms-VI-PZa" collectionClass="NSMutableArray" id="v71-6n-ufG"/>
                <outletCollection property="labels" destination="hgy-52-e1p" collectionClass="NSMutableArray" id="qR2-7b-5tk"/>
                <outletCollection property="slashLabels" destination="LWa-FT-OeY" collectionClass="NSMutableArray" id="XKk-Po-Qvl"/>
            </connections>
        </placeholder>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT" customClass="DateInputView" customModule="JBCalendarDatePicker">
            <rect key="frame" x="0.0" y="0.0" width="439" height="104"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <stackView opaque="NO" contentMode="scaleToFill" spacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="1DW-sK-Kuh">
                    <rect key="frame" x="0.0" y="47" width="178.5" height="20.5"/>
                    <subviews>
                        <stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1tw-Dr-MZK">
                            <rect key="frame" x="0.0" y="0.0" width="88" height="20.5"/>
                            <subviews>
                                <label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="10" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="e10-lT-IGg">
                                    <rect key="frame" x="0.0" y="0.0" width="18" height="20.5"/>
                                    <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
                                    <nil key="textColor"/>
                                    <nil key="highlightedColor"/>
                                </label>
                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="/" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mth-vw-Bki">
                                    <rect key="frame" x="18" y="0.0" width="5.5" height="20.5"/>
                                    <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
                                    <nil key="textColor"/>
                                    <nil key="highlightedColor"/>
                                </label>
                                <label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="29" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CZQ-vO-Hfs">
                                    <rect key="frame" x="23.5" y="0.0" width="20.5" height="20.5"/>
                                    <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
                                    <nil key="textColor"/>
                                    <nil key="highlightedColor"/>
                                </label>
                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="/" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Ms-VI-PZa">
                                    <rect key="frame" x="44" y="0.0" width="5.5" height="20.5"/>
                                    <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
                                    <nil key="textColor"/>
                                    <nil key="highlightedColor"/>
                                </label>
                                <label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2019" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hgy-52-e1p">
                                    <rect key="frame" x="49.5" y="0.0" width="38.5" height="20.5"/>
                                    <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
                                    <nil key="textColor"/>
                                    <nil key="highlightedColor"/>
                                </label>
                            </subviews>
                        </stackView>
                        <stackView opaque="NO" contentMode="scaleToFill" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="sGC-mG-oKf">
                            <rect key="frame" x="102" y="0.0" width="76.5" height="20.5"/>
                            <subviews>
                                <stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KKl-98-e11">
                                    <rect key="frame" x="0.0" y="0.0" width="46.5" height="20.5"/>
                                    <subviews>
                                        <label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="23" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wWz-nR-eEw">
                                            <rect key="frame" x="0.0" y="0.0" width="20.5" height="20.5"/>
                                            <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
                                            <nil key="textColor"/>
                                            <nil key="highlightedColor"/>
                                        </label>
                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=":" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="LWa-FT-OeY">
                                            <rect key="frame" x="20.5" y="0.0" width="5" height="20.5"/>
                                            <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
                                            <nil key="textColor"/>
                                            <nil key="highlightedColor"/>
                                        </label>
                                        <label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="59" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xBH-eh-Kkk">
                                            <rect key="frame" x="25.5" y="0.0" width="21" height="20.5"/>
                                            <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
                                            <nil key="textColor"/>
                                            <nil key="highlightedColor"/>
                                        </label>
                                    </subviews>
                                </stackView>
                                <label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="AM" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Yb6-cX-Iup">
                                    <rect key="frame" x="50.5" y="0.0" width="26" height="20.5"/>
                                    <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
                                    <nil key="textColor"/>
                                    <nil key="highlightedColor"/>
                                </label>
                            </subviews>
                        </stackView>
                    </subviews>
                </stackView>
            </subviews>
            <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
            <constraints>
                <constraint firstItem="1DW-sK-Kuh" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" id="KES-GK-c0X"/>
                <constraint firstItem="1DW-sK-Kuh" firstAttribute="centerY" secondItem="fnl-2z-Ty3" secondAttribute="centerY" id="kl5-dU-SqA"/>
            </constraints>
            <nil key="simulatedTopBarMetrics"/>
            <nil key="simulatedBottomBarMetrics"/>
            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
            <viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
            <point key="canvasLocation" x="155.79710144927537" y="-128.57142857142856"/>
        </view>
    </objects>
</document>


================================================
FILE: Sources/JBCalendarDatePicker/UIColor+SystemAccent.swift
================================================
//
//  UIColor+SystemAccent.swift
//  CalendarDatePickerViewController
//
//  Created by Josh Birnholz on 28/10/2019.
//  Copyright © 2019 Josh Birnholz. All rights reserved.
//

#if canImport(UIKit)
import UIKit

extension UIColor {
	
	#if targetEnvironment(macCatalyst)
	static var systemAccent: UIColor {
		let hasAccentSet = UserDefaults.standard.object(forKey: "AppleAccentColor") != nil
		let systemAccentColor = UserDefaults.standard.integer(forKey: "AppleAccentColor")
		var returnColor: UIColor = UIColor { traitCollection in
				traitCollection.userInterfaceStyle == .dark ? #colorLiteral(red: 0.008315349929, green: 0.3450804651, blue: 0.817365706, alpha: 1) : #colorLiteral(red: 0.01329958253, green: 0.3846624196, blue: 0.8779004216, alpha: 1)
		}
		if hasAccentSet {
			switch systemAccentColor {
				case -1:
					returnColor = UIColor { traitCollection in
							traitCollection.userInterfaceStyle == .dark ? #colorLiteral(red: 0.4039281607, green: 0.403850317, blue: 0.4124818146, alpha: 1) : #colorLiteral(red: 0.5019147992, green: 0.5019902587, blue: 0.5018982291, alpha: 1)
					}
				case 0:
					returnColor = UIColor { traitCollection in
							traitCollection.userInterfaceStyle == .dark ? #colorLiteral(red: 0.82002002, green: 0.2045214176, blue: 0.2204136252, alpha: 1) : #colorLiteral(red: 0.7370213866, green: 0.1443678439, blue: 0.1633504629, alpha: 1)
					}
				case 1:
					returnColor = UIColor { traitCollection in
							traitCollection.userInterfaceStyle == .dark ? #colorLiteral(red: 0.7512640357, green: 0.3605512679, blue: 0.01273573376, alpha: 1) : #colorLiteral(red: 0.8462041616, green: 0.4178547263, blue: 0.05405366421, alpha: 1)
					}
				case 2:
					returnColor = UIColor { traitCollection in
							traitCollection.userInterfaceStyle == .dark ? #colorLiteral(red: 0.8009095192, green: 0.5611655712, blue: 0.05494389683, alpha: 1) : #colorLiteral(red: 0.8690621257, green: 0.6199508309, blue: 0.07889743894, alpha: 1)
					}
				case 3:
					returnColor = UIColor { traitCollection in
							traitCollection.userInterfaceStyle == .dark ? #colorLiteral(red: 0.2549478412, green: 0.5663680434, blue: 0.1645001471, alpha: 1) : #colorLiteral(red: 0.3048421741, green: 0.6298194528, blue: 0.1963118315, alpha: 1)
					}
				case 5:
					returnColor = UIColor { traitCollection in
						traitCollection.userInterfaceStyle == .dark ? #colorLiteral(red: 0.500952661, green: 0.1951716244, blue: 0.5008149147, alpha: 1) : #colorLiteral(red: 0.4900261164, green: 0.1631549001, blue: 0.4976372719, alpha: 1)
				}
				case 6:
					returnColor = UIColor { traitCollection in
							traitCollection.userInterfaceStyle == .dark ? #colorLiteral(red: 0.7823504806, green: 0.1956582665, blue: 0.4722630978, alpha: 1) : #colorLiteral(red: 0.8491325974, green: 0.2301979959, blue: 0.5240355134, alpha: 1)
					}
				default:
					break
			}
		}
		return returnColor
	}
	#endif

	static let lightLabel = UIColor { traitCollection in
		if traitCollection.userInterfaceStyle == .dark {
			return .label
		} else {
			return .systemBackground
		}
	}
}

#endif


================================================
FILE: Tests/JBCalendarDatePickerTests/JBCalendarDatePickerTests.swift
================================================
import XCTest
@testable import JBCalendarDatePicker

final class JBCalendarDatePickerTests: XCTestCase {
    func testExample() {
        // This is an example of a functional test case.
        // Use XCTAssert and related functions to verify your tests produce the correct
        // results.
        
    }

    static var allTests = [
        ("testExample", testExample),
    ]
}


================================================
FILE: Tests/JBCalendarDatePickerTests/XCTestManifests.swift
================================================
import XCTest

#if !canImport(ObjectiveC)
public func allTests() -> [XCTestCaseEntry] {
    return [
        testCase(JBCalendarDatePickerTests.allTests),
    ]
}
#endif
Download .txt
gitextract_jmv4cs2m/

├── .gitignore
├── .swiftpm/
│   └── xcode/
│       ├── package.xcworkspace/
│       │   └── contents.xcworkspacedata
│       └── xcshareddata/
│           └── xcschemes/
│               └── JBCalendarDatePicker.xcscheme
├── JBCalendarDatePicker.podspec
├── LICENSE
├── Package.swift
├── README.md
├── Sources/
│   ├── Info.plist
│   └── JBCalendarDatePicker/
│       ├── CalendarDatePickerViewController.h
│       ├── DateInputView.swift
│       ├── Day.swift
│       ├── JBCalendarDateCell.swift
│       ├── JBCalendarDateCell.xib
│       ├── JBCalendarDatePicker.h
│       ├── JBCalendarViewController.swift
│       ├── JBCalendarViewController.xib
│       ├── JBDatePicker.swift
│       ├── JBDatePickerViewController.swift
│       ├── JBDatePickerViewController.xib
│       └── UIColor+SystemAccent.swift
└── Tests/
    └── JBCalendarDatePickerTests/
        ├── JBCalendarDatePickerTests.swift
        └── XCTestManifests.swift
Condensed preview — 22 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (90K chars).
[
  {
    "path": ".gitignore",
    "chars": 53,
    "preview": ".DS_Store\n/.build\n/Packages\n/*.xcodeproj\nxcuserdata/\n"
  },
  {
    "path": ".swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata",
    "chars": 135,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef"
  },
  {
    "path": ".swiftpm/xcode/xcshareddata/xcschemes/JBCalendarDatePicker.xcscheme",
    "chars": 4093,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1230\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "JBCalendarDatePicker.podspec",
    "chars": 881,
    "preview": "Pod::Spec.new do |s|\n\n# 1\ns.platform = :ios\ns.ios.deployment_target = '13.0'\ns.name = \"JBCalendarDatePicker\"\ns.summary ="
  },
  {
    "path": "LICENSE",
    "chars": 1071,
    "preview": "MIT License\n\nCopyright (c) 2019 Joshua Birnholz\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "Package.swift",
    "chars": 1544,
    "preview": "// swift-tools-version:5.3\n// The swift-tools-version declares the minimum version of Swift required to build this packa"
  },
  {
    "path": "README.md",
    "chars": 3552,
    "preview": "\n# JBCalendarDatePicker\nA replacement for UIDatePicker made for Catalyst.\n\nThis is still a work in progress, there are b"
  },
  {
    "path": "Sources/Info.plist",
    "chars": 769,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Sources/JBCalendarDatePicker/CalendarDatePickerViewController.h",
    "chars": 673,
    "preview": "//\n//  CalendarDatePickerViewController.h\n//  CalendarDatePickerViewController\n//\n//  Created by Josh Birnholz on 28/10/"
  },
  {
    "path": "Sources/JBCalendarDatePicker/DateInputView.swift",
    "chars": 1169,
    "preview": "//\n//  DateInputView.swift\n//  CalendarDatePickerViewController\n//\n//  Created by Josh Birnholz on 10/29/19.\n//  Copyrig"
  },
  {
    "path": "Sources/JBCalendarDatePicker/Day.swift",
    "chars": 1021,
    "preview": "//\n//  Day.swift\n//  CalendarDatePickerViewController\n//\n//  Created by Josh Birnholz on 28/10/2019.\n//  Copyright © 201"
  },
  {
    "path": "Sources/JBCalendarDatePicker/JBCalendarDateCell.swift",
    "chars": 318,
    "preview": "//\n//  CalendarDateCollectionViewCell.swift\n//  CalendarDatePickerViewController\n//\n//  Created by Josh Birnholz on 28/1"
  },
  {
    "path": "Sources/JBCalendarDatePicker/JBCalendarDateCell.xib",
    "chars": 3096,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVe"
  },
  {
    "path": "Sources/JBCalendarDatePicker/JBCalendarDatePicker.h",
    "chars": 531,
    "preview": "//\n//  JBDatePicker.h\n//  JBDatePicker\n//\n//  Created by Josh Birnholz on 10/30/19.\n//  Copyright © 2019 Josh Birnholz. "
  },
  {
    "path": "Sources/JBCalendarDatePicker/JBCalendarViewController.swift",
    "chars": 12556,
    "preview": "//\n//  CalendarDatePickerViewController.swift\n//  Calendar Picker\n//\n//  Created by Josh Birnholz on 10/27/19.\n//  Copyr"
  },
  {
    "path": "Sources/JBCalendarDatePicker/JBCalendarViewController.xib",
    "chars": 19090,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVe"
  },
  {
    "path": "Sources/JBCalendarDatePicker/JBDatePicker.swift",
    "chars": 479,
    "preview": "//\n//  JBDatePicker.swift\n//  CalendarDatePickerViewController\n//\n//  Created by Josh Birnholz on 10/29/19.\n//  Copyrigh"
  },
  {
    "path": "Sources/JBCalendarDatePicker/JBDatePickerViewController.swift",
    "chars": 15442,
    "preview": "//\n//  JBDatePickerViewController.swift\n//  CalendarDatePickerViewController\n//\n//  Created by Josh Birnholz on 28/10/20"
  },
  {
    "path": "Sources/JBCalendarDatePicker/JBDatePickerViewController.xib",
    "chars": 11485,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVe"
  },
  {
    "path": "Sources/JBCalendarDatePicker/UIColor+SystemAccent.swift",
    "chars": 3084,
    "preview": "//\n//  UIColor+SystemAccent.swift\n//  CalendarDatePickerViewController\n//\n//  Created by Josh Birnholz on 28/10/2019.\n//"
  },
  {
    "path": "Tests/JBCalendarDatePickerTests/JBCalendarDatePickerTests.swift",
    "chars": 385,
    "preview": "import XCTest\n@testable import JBCalendarDatePicker\n\nfinal class JBCalendarDatePickerTests: XCTestCase {\n    func testEx"
  },
  {
    "path": "Tests/JBCalendarDatePickerTests/XCTestManifests.swift",
    "chars": 170,
    "preview": "import XCTest\n\n#if !canImport(ObjectiveC)\npublic func allTests() -> [XCTestCaseEntry] {\n    return [\n        testCase(JB"
  }
]

About this extraction

This page contains the full source code of the joshbirnholz/JBCalendarDatePicker GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 22 files (79.7 KB), approximately 21.0k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!