├── DayDatePicker
├── Assets
│ └── .gitkeep
└── Classes
│ ├── .gitkeep
│ ├── Extensions
│ ├── DateFormatter+InitWithFormat.swift
│ ├── UITableView+ReloadAndLayout.swift
│ └── Int+RoundToNearest.swift
│ ├── TimePicker
│ ├── TimePickerViewDelegate.swift
│ ├── TimePickerView.Time.swift
│ └── TimePickerView.swift
│ └── DayDatePicker
│ ├── DayDatePickerViewDelegate.swift
│ ├── DayDatePicker.Date.swift
│ └── DayDatePickerView.swift
├── _Pods.xcodeproj
├── resources
└── screenshots
│ ├── 1.png
│ └── 2.png
├── Example
├── DayDatePicker.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── DayDatePicker-Example.xcscheme
│ └── project.pbxproj
├── Podfile
├── DayDatePicker.xcworkspace
│ └── contents.xcworkspacedata
├── Podfile.lock
├── Tests
│ ├── Info.plist
│ └── Tests.swift
└── DayDatePicker
│ ├── Images.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Info.plist
│ ├── TimePickerViewController.swift
│ ├── DayDatePickerViewController.swift
│ ├── AppDelegate.swift
│ └── Base.lproj
│ ├── LaunchScreen.xib
│ └── Main.storyboard
├── .travis.yml
├── LICENSE
├── DayDatePicker.podspec
├── README.md
└── .gitignore
/DayDatePicker/Assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/DayDatePicker/Classes/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/_Pods.xcodeproj:
--------------------------------------------------------------------------------
1 | Example/Pods/Pods.xcodeproj
--------------------------------------------------------------------------------
/resources/screenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hughbe/day-date-picker/HEAD/resources/screenshots/1.png
--------------------------------------------------------------------------------
/resources/screenshots/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hughbe/day-date-picker/HEAD/resources/screenshots/2.png
--------------------------------------------------------------------------------
/Example/DayDatePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 | use_frameworks!
2 |
3 | target 'DayDatePicker_Example' do
4 | pod 'DayDatePicker', :path => '../'
5 |
6 | target 'DayDatePicker_Tests' do
7 | inherit! :search_paths
8 |
9 | pod 'FBSnapshotTestCase' , '~> 2.1.4'
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/Example/DayDatePicker.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/DayDatePicker/Classes/Extensions/DateFormatter+InitWithFormat.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DateFormatter+InitWithFormat.swift
3 | // DayDatePicker
4 | //
5 | // Created by Hugh Bellamy on 01/02/2018.
6 | //
7 |
8 | import Foundation
9 |
10 | public extension DateFormatter {
11 | convenience init(format: String) {
12 | self.init()
13 | self.dateFormat = format
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | osx_image: xcode9.2
2 | language: objective-c
3 | cache: cocoapods
4 | podfile: Example/Podfile
5 | before_install:
6 | - gem install cocoapods
7 | - pod install --project-directory=Example
8 | script:
9 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/DayDatePicker.xcworkspace -scheme DayDatePicker-Example -destination 'platform=iOS Simulator,name=iPhone 6,OS=11.2' ONLY_ACTIVE_ARCH=NO | xcpretty
10 | - pod lib lint
11 |
--------------------------------------------------------------------------------
/Example/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - DayDatePicker (1.7)
3 | - FBSnapshotTestCase (2.1.4):
4 | - FBSnapshotTestCase/SwiftSupport (= 2.1.4)
5 | - FBSnapshotTestCase/Core (2.1.4)
6 | - FBSnapshotTestCase/SwiftSupport (2.1.4):
7 | - FBSnapshotTestCase/Core
8 |
9 | DEPENDENCIES:
10 | - DayDatePicker (from `../`)
11 | - FBSnapshotTestCase (~> 2.1.4)
12 |
13 | EXTERNAL SOURCES:
14 | DayDatePicker:
15 | :path: ../
16 |
17 | SPEC CHECKSUMS:
18 | DayDatePicker: e493a82e774ae43ec9b825f1a39c3a6b374a885f
19 | FBSnapshotTestCase: 094f9f314decbabe373b87cc339bea235a63e07a
20 |
21 | PODFILE CHECKSUM: 69fcf032d21f0c566bbf962e2e8c2116500e1eda
22 |
23 | COCOAPODS: 1.4.0
24 |
--------------------------------------------------------------------------------
/DayDatePicker/Classes/TimePicker/TimePickerViewDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TimePickerViewDelegate.swift
3 | // DayDatePicker
4 | //
5 | // Created by Hugh Bellamy on 05/02/2018.
6 | //
7 |
8 | import UIKit
9 |
10 | @objc public enum TimePickerViewColumn: Int {
11 | case hour
12 | case minute
13 | }
14 |
15 |
16 | @objc public protocol TimePickerViewDelegate {
17 | @objc optional func customizeCell(cell: UITableViewCell, atIndexPath indexPath: IndexPath, forType type: TimePickerViewColumn)
18 | func didSelectTime(hour: NSInteger, minute: NSInteger)
19 | }
20 |
21 | extension TimePickerViewDelegate {
22 | func customizeCell(cell: UITableViewCell, atIndexPath indexPath: IndexPath, forType type: TimePickerViewColumn) { }
23 | }
24 |
--------------------------------------------------------------------------------
/DayDatePicker/Classes/DayDatePicker/DayDatePickerViewDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DayDatePickerViewDelegate.swift
3 | // DayDatePicker
4 | //
5 | // Created by Hugh Bellamy on 05/02/2018.
6 | //
7 |
8 | import UIKit
9 |
10 | @objc public enum DayDatePickerViewColumn: Int {
11 | case day
12 | case month
13 | case year
14 | }
15 |
16 | @objc public protocol DayDatePickerViewDelegate {
17 | @objc optional func customizeCell(cell: UITableViewCell, atIndexPath indexPath: IndexPath, forType type: DayDatePickerViewColumn)
18 | func didSelectDate(day: NSInteger, month: NSInteger, year: NSInteger)
19 | }
20 |
21 | extension DayDatePickerViewDelegate {
22 | func customizeCell(cell: UITableViewCell, atIndexPath indexPath: IndexPath, forType type: DayDatePickerViewColumn) { }
23 | }
24 |
--------------------------------------------------------------------------------
/Example/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/Tests/Tests.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import XCTest
3 | import DayDatePicker
4 |
5 | class Tests: XCTestCase {
6 |
7 | override func setUp() {
8 | super.setUp()
9 | // Put setup code here. This method is called before the invocation of each test method in the class.
10 | }
11 |
12 | override func tearDown() {
13 | // Put teardown code here. This method is called after the invocation of each test method in the class.
14 | super.tearDown()
15 | }
16 |
17 | func testExample() {
18 | // This is an example of a functional test case.
19 | XCTAssert(true, "Pass")
20 | }
21 |
22 | func testPerformanceExample() {
23 | // This is an example of a performance test case.
24 | self.measure() {
25 | // Put the code you want to measure the time of here.
26 | }
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/DayDatePicker/Classes/Extensions/UITableView+ReloadAndLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UITableView+ReloadAndLayout.swift
3 | // DayDatePicker
4 | //
5 | // Created by Hugh Bellamy on 05/02/2018.
6 | //
7 |
8 | import UIKit
9 |
10 | internal extension UITableView {
11 | func reloadAndLayout() {
12 | reloadData()
13 | setNeedsLayout()
14 | layoutIfNeeded()
15 | }
16 |
17 | func getRowScroll() -> Int {
18 | var relativeOffset = CGPoint(x: 0, y: contentOffset.y + contentInset.top)
19 | relativeOffset.y = min(contentSize.height + contentInset.top, relativeOffset.y)
20 |
21 | let row = Int(round(relativeOffset.y / rowHeight))
22 | return row
23 | }
24 |
25 | func scrollToRow(row: Int, animated: Bool) {
26 | let scroll = CGFloat(row) * rowHeight - contentInset.top
27 | setContentOffset(CGPoint(x: 0, y: scroll), animated: animated)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/DayDatePicker/Classes/Extensions/Int+RoundToNearest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Int+RoundToNearest.swift
3 | // DayDatePicker
4 | //
5 | // Created by Hugh Bellamy on 05/02/2018.
6 | //
7 |
8 | class DayDateIntHelpers {
9 | static let ordinalFormatter: NumberFormatter = {
10 | let formatter = NumberFormatter()
11 | formatter.numberStyle = .ordinal
12 | return formatter
13 | }()
14 | }
15 |
16 | internal extension Int {
17 | func round(toNearest: Int) -> Int{
18 | let fractionNum = Double(self) / Double(toNearest)
19 | let roundedNum = Int(floor(fractionNum))
20 | return roundedNum * toNearest
21 | }
22 |
23 | var ordinalIndicatorString: String {
24 | get {
25 | let stringSelf = "\(self)"
26 | if let ordinalSelf: String = DayDateIntHelpers.ordinalFormatter.string(from: NSNumber(value: self)) {
27 | return ordinalSelf.replacingOccurrences(of: stringSelf, with: "")
28 | }
29 | return ""
30 | }
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 hughbe
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Example/DayDatePicker/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "size" : "1024x1024",
46 | "scale" : "1x"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/DayDatePicker.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'DayDatePicker'
3 | s.version = '1.8'
4 | s.summary = 'A custom and customizable UIDatePicker which displays the day of the week alongside the day column'
5 |
6 | s.description = <<-DESC
7 | A custom and customizable UIDatePicker which displays the day of the week alongside the day column. You can customize the appearance and behaviour of the control.
8 | DESC
9 |
10 | s.homepage = 'https://github.com/hughbe/day-date-picker'
11 | s.screenshots = 'https://raw.githubusercontent.com/hughbe/day-date-picker/master/resources/screenshots/1.png', 'https://raw.githubusercontent.com/hughbe/day-date-picker/master/resources/screenshots/2.png'
12 | s.license = { :type => 'MIT', :file => 'LICENSE' }
13 | s.author = { 'hughbe' => 'hughbellars@gmail.com' }
14 | s.source = { :git => 'https://github.com/hughbe/day-date-picker.git', :tag => s.version.to_s }
15 |
16 | s.ios.deployment_target = '9.0'
17 | s.swift_version = '4.0'
18 |
19 | s.source_files = 'DayDatePicker/Classes/**/*'
20 | s.frameworks = 'UIKit'
21 | end
22 |
--------------------------------------------------------------------------------
/Example/DayDatePicker/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DayDatePicker
2 | A custom and customizable UIDatePicker which displays the day of the week alongside the day column
3 |
4 | ## Screenshots
5 | 
6 | 
7 |
8 | ## Installation
9 |
10 | DayDatePicker is available through [CocoaPods](http://cocoapods.org). To install
11 | it, simply add the following line to your Podfile:
12 |
13 | ```ruby
14 | pod 'DayDatePicker'
15 | ```
16 |
17 | ## Usage
18 | From interface builder, create a UIView and set the class to `TimePickerView` or `DayDatePickerView`. The display will render in interface builder. These views use Auto Layout. Use the `editingChanged` event to receive updates when the date/time was changed.
19 |
20 | DayDatePickerView is highly customizable, making is an ideal replacement for UIDatePicker if working with dates only. You can implement DayDatePickerViewDelegate or TimePickerViewDelegate to customize the display of cells in each column.
21 |
22 | Use the `overlayView` property to access or modify the selection indicator.
23 |
24 | ## Example
25 |
26 | To run the example project, clone the repo, and run `pod install` from the Example directory first.
27 |
--------------------------------------------------------------------------------
/Example/DayDatePicker/TimePickerViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TimePickerViewController.swift
3 | // DayDatePicker
4 | //
5 | // Created by hughbe on 02/01/2018.
6 | // Copyright (c) 2018 hughbe. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import DayDatePicker
11 |
12 | class TimePickerViewController: UIViewController {
13 |
14 | // MARK: - Outlets
15 | @IBOutlet weak var timePickerView: TimePickerView!
16 | @IBOutlet weak var timeLabel: UILabel!
17 |
18 | // MARK: - Variables
19 | private let step = 1
20 |
21 | // MARK: - Override
22 | override func viewDidLoad() {
23 | super.viewDidLoad()
24 |
25 | setup()
26 | }
27 |
28 | // MARK: - Setup
29 | private func setup() {
30 | timePickerView.delegate = self
31 | timePickerView.minuteInterval = step
32 | timePickerView.setMinTime(hour: 10, minute: 23, animated: true)
33 | timePickerView.setMaxTime(hour: 20, minute: 00, animated: true)
34 | }
35 |
36 | // MARK: - Actions
37 | @IBAction func setToTenFifteen(_ sender: Any) {
38 | timePickerView.setTime(hour: 10, minute: 15, animated: true)
39 | }
40 |
41 | @IBAction func setMinTimeToTenThirty(_ sender: Any) {
42 | timePickerView.setMinTime(hour: 10, minute: 30, animated: true)
43 | }
44 |
45 | @IBAction func setOverlayColor(_ sender: Any) {
46 | timePickerView.overlayView.backgroundColor = UIColor.brown
47 | }
48 |
49 | @IBAction func timePickerChanged(_ sender: TimePickerView) {
50 | timeLabel.text = String(format: "%02d:%02d", sender.hour, sender.minute)
51 | }
52 | }
53 |
54 | // MARK: - Time Picker View Delegate
55 | extension TimePickerViewController: TimePickerViewDelegate {
56 | func didSelectTime(hour: NSInteger, minute: NSInteger) {
57 | timeLabel.text = String(format: "%02d:%02d", hour, minute)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Example/DayDatePicker/DayDatePickerViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DayDatePickerViewController.swift
3 | // DayDatePicker
4 | //
5 | // Created by hughbe on 02/01/2018.
6 | // Copyright (c) 2018 hughbe. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import DayDatePicker
11 |
12 | class DayDatePickerViewController: UIViewController {
13 |
14 | // MARK: - Outlets
15 | @IBOutlet weak var dayDatePickerView: DayDatePickerView!
16 | @IBOutlet weak var dayDateLabel: UILabel!
17 |
18 | // MARK: - Override
19 | override func viewDidLoad() {
20 | super.viewDidLoad()
21 |
22 | setup()
23 | }
24 |
25 | // MARK: - Setup
26 | private func setup() {
27 | dayDatePickerView.delegate = self
28 | dayDatePickerView.setMaxDate(Date(), animated: true)
29 | dayDatePickerView.setFeedback(hasHapticFeedback: false, hasSound: false)
30 | dayDatePickerView.backgroundColor = UIColor.lightGray
31 | }
32 |
33 | // MARK: - Actions
34 | @IBAction func setDateTo20February2017(_ sender: Any) {
35 | dayDatePickerView.setDate(year: 2017, month: 2, day: 20, animated: true)
36 | }
37 |
38 | @IBAction func setMinDateTo25February2017(_ sender: Any) {
39 | dayDatePickerView.setMinDate(year: 2017, month: 2, day: 25, animated: true)
40 | }
41 |
42 | @IBAction func setOverlayColor(_ sender: Any) {
43 | dayDatePickerView.overlayView.backgroundColor = UIColor.brown
44 | }
45 |
46 | @IBAction func dayDatePickerChanged(_ sender: DayDatePickerView) {
47 | dayDateLabel.text = String(format: "%02d/%02d/%04d", sender.day, sender.month, sender.year)
48 | }
49 | }
50 |
51 | // MARK: - Day Date Picker View Delegate
52 | extension DayDatePickerViewController: DayDatePickerViewDelegate {
53 | func didSelectDate(day: NSInteger, month: NSInteger, year: NSInteger) {
54 | dayDateLabel.text = String(format: "%02d/%02d/%04d", day, month, year)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Example/DayDatePicker/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // DayDatePicker
4 | //
5 | // Created by hughbe on 02/01/2018.
6 | // Copyright (c) 2018 hughbe. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/DayDatePicker/Classes/TimePicker/TimePickerView.Time.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TimePickerView.Time.swift
3 | // DayDatePicker
4 | //
5 | // Created by Hugh Bellamy on 05/02/2018.
6 | //
7 |
8 | import Foundation
9 |
10 | public extension TimePickerView {
11 | public struct Time: Comparable {
12 | public init(hour: Int, minute: Int) {
13 | self.hour = hour
14 | self.minute = minute
15 | }
16 |
17 | public init(date: Date) {
18 | let components = Calendar.current.dateComponents([.hour, .minute], from: date)
19 |
20 | self.hour = components.hour ?? 0
21 | self.minute = components.minute ?? 0
22 | }
23 |
24 | public var hour: Int {
25 | willSet {
26 | if let hoursInADay = Calendar.current.range(of: .hour, in: .day, for: Date()) {
27 | precondition(newValue >= hoursInADay.lowerBound && newValue < hoursInADay.upperBound)
28 | }
29 | }
30 | }
31 |
32 | public var minute: Int {
33 | willSet {
34 | if let minutesInAnHour = Calendar.current.range(of: .minute, in: .hour, for: Date()) {
35 | precondition(newValue >= minutesInAnHour.lowerBound && newValue < minutesInAnHour.upperBound)
36 | }
37 | }
38 | }
39 |
40 | public var date: Date? {
41 | var components = DateComponents()
42 | components.hour = hour
43 | components.minute = minute
44 |
45 | return Calendar.current.date(from: components)
46 | }
47 |
48 | public static func <(lhs: TimePickerView.Time, rhs: TimePickerView.Time) -> Bool {
49 | if lhs.hour < rhs.hour {
50 | return true
51 | } else if lhs.hour == rhs.hour && lhs.minute < rhs.minute {
52 | return true
53 | }
54 |
55 | return false
56 | }
57 |
58 | public static func ==(lhs: TimePickerView.Time, rhs: TimePickerView.Time) -> Bool {
59 | return lhs.hour == rhs.hour && lhs.minute == rhs.minute
60 | }
61 |
62 | public func time(byAddingHour hour: Int, andMinutes minutes: Int) -> Time? {
63 | guard let date = date else {
64 | return nil
65 | }
66 |
67 | var components = DateComponents()
68 | components.hour = hour
69 | components.minute = minutes
70 |
71 | guard let addedDate = Calendar.current.date(byAdding: components, to: date) else {
72 | return nil
73 | }
74 |
75 | return Time(date: addedDate)
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/DayDatePicker/Classes/DayDatePicker/DayDatePicker.Date.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DayDatePicker.Date.swift
3 | // DayDatePicker
4 | //
5 | // Created by Hugh Bellamy on 05/02/2018.
6 | //
7 |
8 | extension DayDatePickerView {
9 | public struct Date: Comparable {
10 | public init(year: Int, month: Int, day: Int) {
11 | self.year = year
12 | self.month = month
13 | self.day = day
14 | }
15 |
16 | public init(date: Foundation.Date) {
17 | let components = Calendar.current.dateComponents([.year, .month, .day], from: date)
18 |
19 | year = components.year ?? 0
20 | month = components.month ?? 0
21 | day = components.day ?? 0
22 | }
23 |
24 | public var year: Int {
25 | willSet {
26 | if let yearsInEra = Calendar.current.range(of: .year, in: .era, for: date) {
27 | precondition(newValue >= yearsInEra.lowerBound && newValue < yearsInEra.upperBound)
28 | }
29 | }
30 | }
31 |
32 | public var month: Int {
33 | willSet {
34 | if let monthsInYear = Calendar.current.range(of: .month, in: .year, for: date) {
35 | precondition(newValue >= monthsInYear.lowerBound && newValue < monthsInYear.upperBound)
36 | }
37 | }
38 | }
39 |
40 | public var day: Int {
41 | willSet {
42 | if let daysInMonth = Calendar.current.range(of: .day, in: .month, for: date) {
43 | precondition(newValue >= daysInMonth.lowerBound && newValue < daysInMonth.upperBound)
44 | }
45 | }
46 | }
47 |
48 | public var date: Foundation.Date {
49 | get {
50 | var components = DateComponents()
51 | components.year = year
52 | components.month = month
53 | components.day = day
54 |
55 | return Calendar.current.date(from: components)!
56 | }
57 | }
58 |
59 | public static func <(lhs: DayDatePickerView.Date, rhs: DayDatePickerView.Date) -> Bool {
60 | if lhs.year < rhs.year {
61 | return true
62 | } else if lhs.year == rhs.year {
63 | if lhs.month < rhs.month {
64 | return true
65 | } else if lhs.month == rhs.month && lhs.day < rhs.day {
66 | return true
67 | }
68 | }
69 |
70 | return false
71 | }
72 |
73 | public static func ==(lhs: DayDatePickerView.Date, rhs: DayDatePickerView.Date) -> Bool {
74 | return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Example/DayDatePicker/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/osx,macos,swift,xcode,carthage,cocoapods,objective-c
3 |
4 | ### Carthage ###
5 | # Carthage
6 | #
7 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
8 | # Carthage/Checkouts
9 |
10 | Carthage/Build
11 |
12 | ### CocoaPods ###
13 | ## CocoaPods GitIgnore Template
14 |
15 | # CocoaPods - Only use to conserve bandwidth / Save time on Pushing
16 | # - Also handy if you have a large number of dependant pods
17 | # - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGNORE THE LOCK FILE
18 | Pods/
19 |
20 | ### macOS ###
21 | *.DS_Store
22 | .AppleDouble
23 | .LSOverride
24 |
25 | # Icon must end with two \r
26 | Icon
27 |
28 | # Thumbnails
29 | ._*
30 |
31 | # Files that might appear in the root of a volume
32 | .DocumentRevisions-V100
33 | .fseventsd
34 | .Spotlight-V100
35 | .TemporaryItems
36 | .Trashes
37 | .VolumeIcon.icns
38 | .com.apple.timemachine.donotpresent
39 |
40 | # Directories potentially created on remote AFP share
41 | .AppleDB
42 | .AppleDesktop
43 | Network Trash Folder
44 | Temporary Items
45 | .apdisk
46 |
47 | ### Objective-C ###
48 | # Xcode
49 | #
50 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
51 |
52 | ## Build generated
53 | build/
54 | DerivedData/
55 |
56 | ## Various settings
57 | *.pbxuser
58 | !default.pbxuser
59 | *.mode1v3
60 | !default.mode1v3
61 | *.mode2v3
62 | !default.mode2v3
63 | *.perspectivev3
64 | !default.perspectivev3
65 | xcuserdata/
66 |
67 | ## Other
68 | *.moved-aside
69 | *.xccheckout
70 | *.xcscmblueprint
71 |
72 | ## Obj-C/Swift specific
73 | *.hmap
74 | *.ipa
75 | *.dSYM.zip
76 | *.dSYM
77 |
78 | # CocoaPods - Refactored to standalone file
79 |
80 |
81 | # Carthage - Refactored to standalone file
82 |
83 | # fastlane
84 | #
85 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
86 | # screenshots whenever they are needed.
87 | # For more information about the recommended setup visit:
88 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
89 |
90 | fastlane/report.xml
91 | fastlane/Preview.html
92 | fastlane/screenshots
93 | fastlane/test_output
94 |
95 | # Code Injection
96 | #
97 | # After new code Injection tools there's a generated folder /iOSInjectionProject
98 | # https://github.com/johnno1962/injectionforxcode
99 |
100 | iOSInjectionProject/
101 |
102 | ### Objective-C Patch ###
103 |
104 | ### OSX ###
105 |
106 | # Icon must end with two \r
107 |
108 | # Thumbnails
109 |
110 | # Files that might appear in the root of a volume
111 |
112 | # Directories potentially created on remote AFP share
113 |
114 | ### Swift ###
115 | # Xcode
116 | #
117 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
118 |
119 | ## Build generated
120 |
121 | ## Various settings
122 |
123 | ## Other
124 |
125 | ## Obj-C/Swift specific
126 |
127 | ## Playgrounds
128 | timeline.xctimeline
129 | playground.xcworkspace
130 |
131 | # Swift Package Manager
132 | #
133 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
134 | # Packages/
135 | # Package.pins
136 | .build/
137 |
138 | # CocoaPods - Refactored to standalone file
139 |
140 | # Carthage - Refactored to standalone file
141 |
142 | # fastlane
143 | #
144 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
145 | # screenshots whenever they are needed.
146 | # For more information about the recommended setup visit:
147 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
148 |
149 |
150 | ### Xcode ###
151 | # Xcode
152 | #
153 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
154 |
155 | ## Build generated
156 |
157 | ## Various settings
158 |
159 | ## Other
160 |
161 | ### Xcode Patch ###
162 | *.xcodeproj/*
163 | !*.xcodeproj/project.pbxproj
164 | !*.xcodeproj/xcshareddata/
165 | !*.xcworkspace/contents.xcworkspacedata
166 | /*.gcno
167 |
168 |
169 | # End of https://www.gitignore.io/api/osx,macos,swift,xcode,carthage,cocoapods,objective-c
--------------------------------------------------------------------------------
/Example/DayDatePicker.xcodeproj/xcshareddata/xcschemes/DayDatePicker-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
45 |
46 |
48 |
54 |
55 |
56 |
57 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
80 |
82 |
88 |
89 |
90 |
91 |
92 |
93 |
99 |
101 |
107 |
108 |
109 |
110 |
112 |
113 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/DayDatePicker/Classes/TimePicker/TimePickerView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TimePickerView.swift
3 | // DayDatePicker
4 | //
5 | // Created by Hugh Bellamy on 01/02/2018.
6 | //
7 |
8 | import UIKit
9 | import AudioToolbox // Sound list: https://github.com/klaas/SwiftySystemSounds
10 |
11 | @IBDesignable
12 | public class TimePickerView: UIControl {
13 |
14 | // MARK: - Init
15 | public override init(frame: CGRect) {
16 | super.init(frame: frame)
17 | setup()
18 | }
19 |
20 | public required init?(coder aDecoder: NSCoder) {
21 | super.init(coder: aDecoder)
22 | }
23 |
24 | // MARK: - Private Property
25 | fileprivate var _time: Time!
26 | fileprivate var _minTime: Time?
27 | fileprivate var _maxTime: Time?
28 | fileprivate var _textColor: UIColor?
29 | fileprivate var _textFont: UIFont?
30 | public let overlayView = UIView()
31 | private let MaxHour = 24
32 | private let MaxMinute = 60
33 | private let MinHour = 0
34 | private let MinMinute = 0
35 | public var hasHapticFeedback: Bool = true
36 | public var hasSound: Bool = true
37 |
38 | // MARK: - Table View Property
39 | fileprivate let hourTableView = UITableView()
40 | fileprivate let minuteTableView = UITableView()
41 | fileprivate var rowHeight: CGFloat = 44
42 | fileprivate var hourRange: Range!
43 | fileprivate var minuteRange: Range!
44 |
45 | // MARK: - Delegate
46 | @IBOutlet public var delegate: TimePickerViewDelegate?
47 |
48 | // MARK: - Public Property
49 | public var minTime: Time? {
50 | get {
51 | return _minTime
52 | } set {
53 | setMinTime(minTime: newValue, animated: true)
54 | }
55 | }
56 |
57 | public var maxTime: Time? {
58 | get {
59 | return _maxTime
60 | } set {
61 | setMinTime(minTime: newValue, animated: true)
62 | }
63 | }
64 |
65 | public var time: Time {
66 | get {
67 | return _time
68 | } set {
69 | setTime(time: newValue, animated: true)
70 | }
71 | }
72 |
73 | public var textFont: UIFont {
74 | get {
75 | return _textFont ?? UIFont.systemFont(ofSize: 20)
76 | } set {
77 | setTextWith(font: newValue, color: _textColor)
78 | }
79 | }
80 |
81 | public var textColor: UIColor {
82 | get {
83 | return _textColor ?? .black
84 | } set {
85 | setTextWith(font: _textFont, color: newValue)
86 | }
87 | }
88 |
89 | @IBInspectable
90 | public var minuteInterval: NSInteger = 5 {
91 | willSet {
92 | if let secondsInMinute = NSCalendar.current.range(of: .second, in: .minute, for: Date()) {
93 | precondition(newValue >= secondsInMinute.lowerBound && secondsInMinute.upperBound % newValue == 0, "The time interval has to be a positive number. 60 must be divisible by the interval.")
94 | }
95 | } didSet {
96 | minuteTableView.reloadData()
97 | }
98 | }
99 |
100 | // MARK: - Public methods
101 | public func setTime(hour: Int, minute: Int, animated: Bool) {
102 | let time = Time(hour: hour, minute: minute)
103 | setTime(time: time, animated: animated)
104 | }
105 |
106 | public func setTime(date: Foundation.Date, animated: Bool) {
107 | let time = Time(date: date)
108 | setTime(time: time, animated: animated)
109 | }
110 |
111 | public func setTime(time: Time, animated: Bool) {
112 | var time = time
113 | if let minTime = _minTime, time < minTime {
114 | time = minTime
115 | } else if let maxTime = _maxTime, time > maxTime {
116 | time = maxTime
117 | }
118 |
119 | time.minute = max(0, min(59, time.minute.round(toNearest: minuteInterval)))
120 | let reloadMinuteTableView = time.hour != _time.hour
121 | _time = time
122 |
123 | if reloadMinuteTableView {
124 | minuteTableView.reloadAndLayout()
125 | }
126 |
127 | if hourTableView.superview != nil {
128 | hourTableView.scrollToRow(row: time.hour, animated: animated)
129 | minuteTableView.scrollToRow(row: time.minute / minuteInterval, animated: animated)
130 | }
131 |
132 | sendActions(for: .editingChanged)
133 | }
134 |
135 | // MARK: - Set Min Time
136 | public func setMinTime(hour: Int, minute: Int, animated: Bool) {
137 | let minTime = Time(hour: hour, minute: minute)
138 | setMinTime(minTime: minTime, animated: animated)
139 | }
140 |
141 | public func setMinTime(_ time: Foundation.Date, animated: Bool) {
142 | let minTime = Time(date: time)
143 | setMinTime(minTime: minTime, animated: true)
144 | }
145 |
146 | public func setMinTime(minTime: Time?, animated: Bool) {
147 | _minTime = minTime
148 | reload()
149 |
150 | if let minTime = minTime, time < minTime {
151 | setTime(time: minTime, animated: animated)
152 | }
153 | }
154 |
155 | // MARK: - Set Max Time
156 | public func setMaxTime(hour: Int, minute: Int, animated: Bool) {
157 | let maxTime = Time(hour: hour, minute: minute)
158 | setMaxTime(maxTime: maxTime, animated: animated)
159 | }
160 |
161 | public func setMaxTime(_ time: Foundation.Date, animated: Bool) {
162 | let maxTime = Time(date: time)
163 | setMaxTime(maxTime: maxTime, animated: true)
164 | }
165 |
166 | public func setMaxTime(maxTime: Time?, animated: Bool) {
167 | _maxTime = maxTime
168 | reload()
169 |
170 | if let maxTime = maxTime, time > maxTime {
171 | setTime(time: maxTime, animated: animated)
172 | }
173 | }
174 |
175 | // MARK: - Set Text
176 | public func setTextWith(font: UIFont?, color: UIColor?) {
177 | _textFont = font ?? UIFont.systemFont(ofSize: 20)
178 | _textColor = color ?? .black
179 |
180 | reload()
181 | }
182 |
183 | // MARK: - Set Feedback
184 | public func setFeedback(hasHapticFeedback: Bool = true, hasSound: Bool = true) {
185 | self.hasHapticFeedback = hasHapticFeedback
186 | self.hasSound = hasSound
187 | }
188 | }
189 |
190 | // Layout actions.
191 | extension TimePickerView {
192 | // MARK: - Override Interface Builder
193 | public override func prepareForInterfaceBuilder() {
194 | super.prepareForInterfaceBuilder()
195 | setup()
196 | }
197 | public override func awakeFromNib() {
198 | super.awakeFromNib()
199 | setup()
200 | }
201 | public override func layoutSubviews() {
202 | super.layoutSubviews()
203 |
204 | let contentInset = UIEdgeInsets(top: (frame.size.height - rowHeight) / 2, left: 0, bottom: (frame.size.height - rowHeight) / 2, right: 0)
205 | hourTableView.contentInset = contentInset
206 | minuteTableView.contentInset = contentInset
207 |
208 | hourTableView.separatorStyle = .none
209 | minuteTableView.separatorStyle = .none
210 |
211 | setTime(time: _time, animated: false)
212 | }
213 |
214 | // MARK: - Setup
215 | fileprivate func setup() {
216 | if hourTableView.superview != nil {
217 | return
218 | }
219 |
220 | setupTableView(tableView: hourTableView)
221 | setupTableView(tableView: minuteTableView)
222 |
223 | overlayView.backgroundColor = UIColor(red: 0, green: 153 / 255, blue: 102 / 255, alpha: 1)
224 | overlayView.isUserInteractionEnabled = false
225 | overlayView.translatesAutoresizingMaskIntoConstraints = false
226 | overlayView.alpha = 0.5
227 | addSubview(overlayView)
228 |
229 | addConstraints([
230 | NSLayoutConstraint(item: hourTableView, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 0),
231 | NSLayoutConstraint(item: hourTableView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0),
232 | NSLayoutConstraint(item: hourTableView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0),
233 | NSLayoutConstraint(item: hourTableView, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 0.5, constant: 0),
234 |
235 | NSLayoutConstraint(item: minuteTableView, attribute: .leading, relatedBy: .equal, toItem: hourTableView, attribute: .trailing, multiplier: 1, constant: 0),
236 | NSLayoutConstraint(item: minuteTableView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0),
237 | NSLayoutConstraint(item: minuteTableView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0),
238 | NSLayoutConstraint(item: minuteTableView, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: 0),
239 |
240 | NSLayoutConstraint(item: overlayView, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 0),
241 | NSLayoutConstraint(item: overlayView, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: 0),
242 | NSLayoutConstraint(item: overlayView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0),
243 | NSLayoutConstraint(item: overlayView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: rowHeight)
244 | ])
245 |
246 | if _time == nil {
247 | let components = Calendar.current.dateComponents([.hour, .minute], from: Date()) as NSDateComponents
248 | _time = Time(hour: components.hour, minute: components.minute)
249 | }
250 |
251 | hourRange = Calendar.current.range(of: .hour, in: .day, for: Date())
252 | minuteRange = Calendar.current.range(of: .minute, in: .hour, for: Date())
253 | }
254 |
255 | private func setupTableView(tableView: UITableView) {
256 | tableView.translatesAutoresizingMaskIntoConstraints = false
257 | tableView.rowHeight = rowHeight
258 | tableView.estimatedRowHeight = rowHeight
259 | tableView.showsVerticalScrollIndicator = false
260 | tableView.backgroundColor = UIColor.white
261 | tableView.contentInset = UIEdgeInsets(top: (frame.size.height - rowHeight) / 2, left: 0, bottom: (frame.size.height - rowHeight) / 2, right: 0)
262 |
263 | tableView.delegate = self
264 | tableView.dataSource = self
265 |
266 | tableView.scrollsToTop = false
267 |
268 | addSubview(tableView)
269 | }
270 | }
271 |
272 | // MARK: - Table View Data Source
273 | extension TimePickerView: UITableViewDataSource {
274 | public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
275 | if tableView == hourTableView {
276 | if let hoursInDay = Calendar.current.range(of: .hour, in: .day, for: Date()) {
277 | hourRange = hoursInDay
278 | return hoursInDay.count
279 | }
280 | } else if tableView == minuteTableView {
281 | if let minutesInAnHour = Calendar.current.range(of: .minute, in: .hour, for: Date()) {
282 | minuteRange = minutesInAnHour
283 | return minutesInAnHour.count / minuteInterval
284 | }
285 | }
286 |
287 | return 0
288 | }
289 |
290 | public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
291 | let cell = UITableViewCell(style: .default, reuseIdentifier: "Cell")
292 | cell.selectionStyle = .none
293 | cell.textLabel?.textAlignment = .center
294 | cell.textLabel?.font = _textFont
295 | cell.backgroundColor = UIColor.white
296 | cell.textLabel?.textColor = _textColor
297 |
298 | if tableView == hourTableView {
299 | let hour = hourRange.lowerBound + indexPath.row
300 | if hour < minHour {
301 | cell.textLabel?.textColor = UIColor.lightGray
302 | } else if hour > maxHour {
303 | cell.textLabel?.textColor = UIColor.lightGray
304 | }
305 |
306 | cell.textLabel?.text = String(hour)
307 |
308 | delegate?.customizeCell(cell: cell, atIndexPath: indexPath, forType: .hour)
309 | } else if tableView == minuteTableView {
310 | let minute = minuteRange.lowerBound + indexPath.row * minuteInterval
311 | let time = Time(hour: hour, minute: minute)
312 | if let minTime = minTime, time < minTime {
313 | cell.textLabel?.textColor = UIColor.lightGray
314 | } else if let maxTime = maxTime, time > maxTime {
315 | cell.textLabel?.textColor = UIColor.lightGray
316 | }
317 |
318 | cell.textLabel?.text = String(time.minute)
319 |
320 | delegate?.customizeCell(cell: cell, atIndexPath: indexPath, forType: .minute)
321 | }
322 |
323 | return cell
324 | }
325 | }
326 |
327 | // MARK: - Table View Delegate
328 | extension TimePickerView: UITableViewDelegate {
329 | public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
330 | return rowHeight
331 | }
332 |
333 | public func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
334 | return rowHeight
335 | }
336 |
337 | public func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
338 | if tableView.isDragging {
339 | if #available(iOS 10.0, *), hasHapticFeedback {
340 | let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
341 | selectionFeedbackGenerator.selectionChanged()
342 | }
343 |
344 | if hasSound {
345 | var urlString: String = ""
346 | if tableView == hourTableView {
347 | urlString = "System/Library/Audio/UISounds/nano/TimerWheelHoursDetent_Haptic.caf"
348 | } else if tableView == minuteTableView {
349 | urlString = "System/Library/Audio/UISounds/nano/TimerWheelMinutesDetent_Haptic.caf"
350 | }
351 |
352 | let url = URL(fileURLWithPath: urlString)
353 | var soundID: SystemSoundID = 0
354 | AudioServicesCreateSystemSoundID(url as CFURL, &soundID)
355 | AudioServicesPlaySystemSound(soundID)
356 | }
357 | }
358 | }
359 |
360 | public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
361 | guard let tableView = scrollView as? UITableView else {
362 | return
363 | }
364 |
365 | if !decelerate {
366 | alignTableViewToRow(tableView: tableView)
367 | }
368 | }
369 |
370 | public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
371 | guard let tableView = scrollView as? UITableView else {
372 | return
373 | }
374 |
375 | alignTableViewToRow(tableView: tableView)
376 | }
377 |
378 | public func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
379 | guard let tableView = scrollView as? UITableView else {
380 | return
381 | }
382 |
383 | alignTableViewToRow(tableView: tableView)
384 | }
385 |
386 | private func alignTableViewToRow(tableView: UITableView) {
387 | let row = tableView.getRowScroll()
388 |
389 | if tableView == hourTableView {
390 | hour = hourRange.lowerBound + row
391 | } else if tableView == minuteTableView {
392 | minute = minuteRange.lowerBound + row * minuteInterval
393 | }
394 |
395 | delegate?.didSelectTime(hour: hour, minute: minute)
396 | }
397 |
398 | public func reload() {
399 | hourTableView.reloadAndLayout()
400 | minuteTableView.reloadAndLayout()
401 | }
402 | }
403 |
404 | // Interface Builder properties.
405 | extension TimePickerView {
406 | @IBInspectable
407 | public var minHour: NSInteger {
408 | get {
409 | return _minTime?.hour ?? MinHour
410 | }
411 | set {
412 | setMinTime(hour: newValue, minute: _minTime?.minute ?? MinMinute, animated: true)
413 | }
414 | }
415 |
416 | @IBInspectable
417 | public var minMinute: NSInteger {
418 | get {
419 | return _minTime?.minute ?? MinMinute
420 | }
421 | set {
422 | setMinTime(hour: _minTime?.hour ?? MinHour, minute: newValue, animated: true)
423 | }
424 | }
425 |
426 | @IBInspectable
427 | public var maxHour: NSInteger {
428 | get {
429 | return _maxTime?.hour ?? MaxHour
430 | }
431 | set {
432 | setMinTime(hour: newValue, minute: _maxTime?.minute ?? MaxMinute, animated: true)
433 | }
434 | }
435 |
436 | @IBInspectable
437 | public var maxMinute: NSInteger {
438 | get {
439 | return _maxTime?.minute ?? MaxMinute
440 | }
441 | set {
442 | setMinTime(hour: _maxTime?.hour ?? MaxHour, minute: newValue, animated: true)
443 | }
444 | }
445 |
446 | @IBInspectable
447 | public var hour: NSInteger {
448 | get {
449 | return time.hour
450 | }
451 | set {
452 | setTime(hour: newValue, minute: time.minute, animated: true)
453 | }
454 | }
455 |
456 | @IBInspectable
457 | public var minute: NSInteger {
458 | get {
459 | return time.minute
460 | }
461 | set {
462 | setTime(hour: time.hour, minute: newValue, animated: true)
463 | }
464 | }
465 | }
466 |
--------------------------------------------------------------------------------
/DayDatePicker/Classes/DayDatePicker/DayDatePickerView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DayDatePickerView
3 | // DayDatePicker
4 | //
5 | // Created by Hugh Bellamy on 01/02/2018.
6 | //
7 |
8 | import UIKit
9 | import AudioToolbox // Sound list: https://github.com/klaas/SwiftySystemSounds
10 |
11 | @IBDesignable
12 | public class DayDatePickerView: UIControl {
13 |
14 | // MARK: - Init
15 | public override init(frame: CGRect) {
16 | super.init(frame: frame)
17 |
18 | setup()
19 | }
20 | public required init?(coder aDecoder: NSCoder) {
21 | super.init(coder: aDecoder)
22 | }
23 |
24 | // MARK: - Private Property
25 | fileprivate var _date: Date!
26 | fileprivate var _minDate: Date?
27 | fileprivate var _maxDate: Date?
28 | fileprivate var _textColor: UIColor?
29 | fileprivate var _textFont: UIFont?
30 | public let overlayView = UIView()
31 | private let MaxDay = 365
32 | private let MaxMonth = 12
33 | private let MaxYear = NSInteger.max
34 | private let MinDay = 0
35 | private let MinMonth = 0
36 | private let MinYear = 0
37 | public var hasHapticFeedback: Bool = true
38 | public var hasSound: Bool = true
39 |
40 | // MARK: - Table View Property
41 | fileprivate let dayTableView = UITableView()
42 | fileprivate let monthTableView = UITableView()
43 | fileprivate let yearTableView = UITableView()
44 | fileprivate var rowHeight: CGFloat = 44
45 | fileprivate var yearRange: Range!
46 | fileprivate var monthRange: Range!
47 | fileprivate var dayRange: Range!
48 |
49 | public var dayFormatter = DateFormatter(format: "EEE d") {
50 | didSet {
51 | dayTableView.reloadData()
52 | }
53 | }
54 |
55 | public var monthFormatter = DateFormatter(format: "MMM") {
56 | didSet {
57 | monthTableView.reloadData()
58 | }
59 | }
60 |
61 | public var yearFormatter = DateFormatter(format: "yyyy") {
62 | didSet {
63 | yearTableView.reloadData()
64 | }
65 | }
66 |
67 | // MARK: - Delegate
68 | @IBOutlet public weak var delegate: DayDatePickerViewDelegate?
69 |
70 | // MARK: - Public Property
71 | public var minDate: Date? {
72 | get {
73 | return _minDate
74 | } set {
75 | setMinDate(minDate: newValue, animated: true)
76 | }
77 | }
78 |
79 | public var maxDate: Date? {
80 | get {
81 | return _maxDate
82 | } set {
83 | setMaxDate(maxDate: newValue, animated: true)
84 | }
85 | }
86 |
87 | public var date: Date {
88 | get {
89 | return _date
90 | } set {
91 | setDate(date: newValue, animated: true)
92 | }
93 | }
94 |
95 | public var textFont: UIFont {
96 | get {
97 | return _textFont ?? UIFont.systemFont(ofSize: 20)
98 | } set {
99 | setTextWith(font: newValue, color: _textColor)
100 | }
101 | }
102 |
103 | public var textColor: UIColor {
104 | get {
105 | return _textColor ?? .black
106 | } set {
107 | setTextWith(font: textFont, color: newValue)
108 | }
109 | }
110 |
111 | override public var backgroundColor: UIColor? {
112 | didSet {
113 | dayTableView.backgroundColor = backgroundColor
114 | dayTableView.reloadData()
115 | monthTableView.backgroundColor = backgroundColor
116 | monthTableView.reloadData()
117 | yearTableView.backgroundColor = backgroundColor
118 | yearTableView.reloadData()
119 | }
120 | }
121 |
122 | public var showOrdinalIndicator = true {
123 | didSet {
124 | dayTableView.reloadData()
125 | }
126 | }
127 |
128 | public func setDate(year: Int, month: Int, day: Int, animated: Bool) {
129 | let date = Date(year: year, month: month, day: day)
130 | setDate(date: date, animated: animated)
131 | }
132 |
133 | public func setDate(date: Foundation.Date, animated: Bool) {
134 | let dateDate = Date(date: date)
135 | setDate(date: dateDate, animated: animated)
136 | }
137 |
138 | // MARK: - Public methods
139 | public func setDate(date: Date, animated: Bool) {
140 | var date = date
141 | if let minTime = _minDate, date < minTime {
142 | date = minTime
143 | } else if let maxDate = _maxDate, date > maxDate {
144 | date = maxDate
145 | }
146 |
147 | let reloadMonthTableView = date.year != _date.year
148 | let reloadDayTableView = reloadMonthTableView || date.month != _date.month
149 |
150 | _date = date
151 |
152 | if reloadMonthTableView {
153 | monthTableView.reloadAndLayout()
154 | }
155 | if reloadDayTableView {
156 | let startMonth = Date(year: year, month: month, day: 1)
157 | if let selectedMonthRange = Calendar.current.range(of: .day, in: .month, for: startMonth.date),
158 | let dataRange = Calendar.current.range(of: .day, in: .month, for: date.date),
159 | dataRange.count > selectedMonthRange.count {
160 | date = Date(year: year, month: month, day: selectedMonthRange.count)
161 | _date = date
162 | }
163 |
164 | dayTableView.reloadAndLayout()
165 | }
166 |
167 | if dayTableView.superview != nil {
168 | dayTableView.scrollToRow(row: date.day - dayRange.lowerBound, animated: animated)
169 | monthTableView.scrollToRow(row: date.month - monthRange.lowerBound, animated: animated)
170 | yearTableView.scrollToRow(row: date.year - yearRange.lowerBound, animated: animated)
171 | }
172 |
173 | sendActions(for: .editingChanged)
174 | }
175 |
176 | // MARK: - Set Min Date
177 | public func setMinDate(year: Int, month: Int, day: Int, animated: Bool) {
178 | let minDate = Date(year: year, month: month, day: day)
179 | setMinDate(minDate: minDate, animated: animated)
180 | }
181 |
182 | public func setMinDate(_ date: Foundation.Date, animated: Bool) {
183 | let minDate = Date(date: date)
184 | setMinDate(minDate: minDate, animated: animated)
185 | }
186 |
187 | public func setMinDate(minDate: Date?, animated: Bool) {
188 | _minDate = minDate
189 | reload()
190 |
191 | if let minDate = minDate, date < minDate {
192 | setDate(date: minDate, animated: true)
193 | }
194 | }
195 |
196 | // MARK: - Set Max Date
197 | public func setMaxDate(year: Int, month: Int, day: Int, animated: Bool) {
198 | let maxDate = Date(year: year, month: month, day: day)
199 | setMaxDate(maxDate: maxDate, animated: true)
200 | }
201 |
202 | public func setMaxDate(_ date: Foundation.Date, animated: Bool) {
203 | let maxDate = Date(date: date)
204 | setMaxDate(maxDate: maxDate, animated: animated)
205 | }
206 |
207 | public func setMaxDate(maxDate: Date?, animated: Bool) {
208 | _maxDate = maxDate
209 | reload()
210 |
211 | if let maxDate = maxDate, date > maxDate {
212 | setDate(date: maxDate, animated: true)
213 | }
214 | }
215 |
216 | // MARK: - Set Text
217 | public func setTextWith(font: UIFont?, color: UIColor?) {
218 | _textFont = font ?? UIFont.systemFont(ofSize: 20)
219 | _textColor = color ?? .black
220 |
221 | reload()
222 | }
223 |
224 | // MARK: - Set Feedback
225 | public func setFeedback(hasHapticFeedback: Bool = true, hasSound: Bool = true) {
226 | self.hasHapticFeedback = hasHapticFeedback
227 | self.hasSound = hasSound
228 | }
229 | }
230 |
231 | // Layout actions.
232 | extension DayDatePickerView {
233 | // MARK: - Override Interface Builder
234 | public override func prepareForInterfaceBuilder() {
235 | super.prepareForInterfaceBuilder()
236 |
237 | setup()
238 | }
239 | public override func awakeFromNib() {
240 | super.awakeFromNib()
241 |
242 | setup()
243 | }
244 | public override func layoutSubviews() {
245 | super.layoutSubviews()
246 |
247 | let contentInset = UIEdgeInsets(top: (frame.size.height - rowHeight) / 2, left: 0, bottom: (frame.size.height - rowHeight) / 2, right: 0)
248 | dayTableView.contentInset = contentInset
249 | monthTableView.contentInset = contentInset
250 | yearTableView.contentInset = contentInset
251 |
252 | dayTableView.separatorStyle = .none
253 | monthTableView.separatorStyle = .none
254 | yearTableView.separatorStyle = .none
255 |
256 | setDate(date: _date, animated: false)
257 | }
258 |
259 | // MARK: - Setup
260 | fileprivate func setup() {
261 | if yearTableView.superview != nil {
262 | return
263 | }
264 |
265 | setupTableView(tableView: dayTableView)
266 | setupTableView(tableView: monthTableView)
267 | setupTableView(tableView: yearTableView)
268 |
269 | overlayView.backgroundColor = UIColor(red: 0, green: 153 / 255, blue: 102 / 255, alpha: 1)
270 | overlayView.isUserInteractionEnabled = false
271 | overlayView.translatesAutoresizingMaskIntoConstraints = false
272 | overlayView.alpha = 0.5
273 | addSubview(overlayView)
274 |
275 | addConstraints([
276 | NSLayoutConstraint(item: dayTableView, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 0),
277 | NSLayoutConstraint(item: dayTableView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0),
278 | NSLayoutConstraint(item: dayTableView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0),
279 | NSLayoutConstraint(item: dayTableView, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 0.4, constant: 0),
280 |
281 | NSLayoutConstraint(item: monthTableView, attribute: .leading, relatedBy: .equal, toItem: dayTableView, attribute: .trailing, multiplier: 1, constant: 0),
282 | NSLayoutConstraint(item: monthTableView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0),
283 | NSLayoutConstraint(item: monthTableView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0),
284 | NSLayoutConstraint(item: monthTableView, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1 / 3, constant: 0),
285 |
286 | NSLayoutConstraint(item: yearTableView, attribute: .leading, relatedBy: .equal, toItem: monthTableView, attribute: .trailing, multiplier: 1, constant: 0),
287 | NSLayoutConstraint(item: yearTableView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0),
288 | NSLayoutConstraint(item: yearTableView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0),
289 | NSLayoutConstraint(item: yearTableView, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: 0),
290 |
291 | NSLayoutConstraint(item: overlayView, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 0),
292 | NSLayoutConstraint(item: overlayView, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: 0),
293 | NSLayoutConstraint(item: overlayView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0),
294 | NSLayoutConstraint(item: overlayView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: rowHeight)
295 | ])
296 |
297 | if _date == nil {
298 | let components = Calendar.current.dateComponents([.year, .month, .day], from: Foundation.Date()) as NSDateComponents
299 | _date = Date(year: components.year, month: components.month, day: components.day)
300 | }
301 |
302 | dayRange = Calendar.current.range(of: .day, in: .month, for: _date.date)
303 | monthRange = Calendar.current.range(of: .month, in: .year, for: _date.date)
304 | yearRange = Calendar.current.range(of: .year, in: .era, for: _date.date)
305 | }
306 |
307 | private func setupTableView(tableView: UITableView) {
308 | tableView.translatesAutoresizingMaskIntoConstraints = false
309 | tableView.rowHeight = rowHeight
310 | tableView.showsVerticalScrollIndicator = false
311 | tableView.backgroundColor = self.backgroundColor
312 |
313 | tableView.delegate = self
314 | tableView.dataSource = self
315 |
316 | tableView.scrollsToTop = false
317 |
318 | addSubview(tableView)
319 | }
320 | }
321 |
322 | // MARK: - Table View Data Source
323 | extension DayDatePickerView: UITableViewDataSource {
324 | public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
325 | if tableView == dayTableView {
326 | if let daysInAMonth = Calendar.current.range(of: .day, in: .month, for: date.date) {
327 | dayRange = daysInAMonth
328 | return daysInAMonth.count
329 | }
330 | } else if tableView == monthTableView {
331 | if let monthsInAYear = Calendar.current.range(of: .month, in: .year, for: date.date) {
332 | monthRange = monthsInAYear
333 | return monthsInAYear.count
334 | }
335 | } else if tableView == yearTableView {
336 | if let yearsInAnEra = Calendar.current.range(of: .year, in: .era, for: date.date) {
337 | yearRange = yearsInAnEra
338 | return yearsInAnEra.count
339 | }
340 | }
341 |
342 | return 0
343 | }
344 |
345 | public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
346 | let cell = UITableViewCell(style: .default, reuseIdentifier: "Cell")
347 | cell.selectionStyle = .none
348 |
349 | cell.textLabel?.textAlignment = .center
350 | cell.textLabel?.font = textFont
351 | cell.backgroundColor = self.backgroundColor
352 | cell.textLabel?.textColor = textColor
353 |
354 | if tableView == dayTableView {
355 | let date = Date(year: year, month: month, day: dayRange.lowerBound + indexPath.row)
356 | if let minDate = minDate, date < minDate {
357 | cell.textLabel?.textColor = UIColor.lightGray
358 | } else if let maxDate = maxDate, date > maxDate {
359 | cell.textLabel?.textColor = UIColor.lightGray
360 | }
361 |
362 | var dayString = dayFormatter.string(from: date.date)
363 | if showOrdinalIndicator {
364 | dayString.append(date.day.ordinalIndicatorString)
365 | }
366 |
367 | cell.textLabel?.text = dayString
368 |
369 | delegate?.customizeCell(cell: cell, atIndexPath: indexPath, forType: .day)
370 | } else if tableView == monthTableView {
371 | let month = monthRange.lowerBound + indexPath.row
372 | if year < minYear || (year == minYear && month < minMonth) {
373 | cell.textLabel?.textColor = UIColor.lightGray
374 | } else if year > maxYear || (year == maxYear && month > maxMonth) {
375 | cell.textLabel?.textColor = UIColor.lightGray
376 | }
377 |
378 | let date = Date(year: year, month: month, day: 1)
379 | cell.textLabel?.text = monthFormatter.string(from: date.date)
380 |
381 | delegate?.customizeCell(cell: cell, atIndexPath: indexPath, forType: .month)
382 | } else if tableView == yearTableView {
383 | let year = yearRange.lowerBound + indexPath.row
384 | if year < minYear {
385 | cell.textLabel?.textColor = UIColor.lightGray
386 | } else if year > maxYear {
387 | cell.textLabel?.textColor = UIColor.lightGray
388 | }
389 |
390 | let date = Date(year: year, month: 1, day: 1)
391 | cell.textLabel?.text = yearFormatter.string(from: date.date)
392 |
393 | delegate?.customizeCell(cell: cell, atIndexPath: indexPath, forType: .year)
394 | }
395 |
396 | return cell
397 | }
398 | }
399 |
400 | // Table view data.
401 | extension DayDatePickerView: UITableViewDelegate {
402 | public func reload() {
403 | dayTableView.reloadAndLayout()
404 | monthTableView.reloadAndLayout()
405 | yearTableView.reloadAndLayout()
406 | }
407 |
408 | public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
409 | if tableView == dayTableView {
410 | day = dayRange.lowerBound + indexPath.row
411 | } else if tableView == monthTableView {
412 | month = monthRange.lowerBound + indexPath.row
413 | } else if tableView == yearTableView {
414 | year = yearRange.lowerBound + indexPath.row
415 | }
416 | delegate?.didSelectDate(day: day, month: month, year: year)
417 | }
418 |
419 | public func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
420 | if tableView.isDragging {
421 | if #available(iOS 10.0, *), hasHapticFeedback {
422 | let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
423 | selectionFeedbackGenerator.selectionChanged()
424 | }
425 |
426 | if hasSound {
427 | let urlString = "System/Library/Audio/UISounds/nano/TimerStart_Haptic.caf"
428 | let url = URL(fileURLWithPath: urlString)
429 | var soundID: SystemSoundID = 0
430 | AudioServicesCreateSystemSoundID(url as CFURL, &soundID)
431 | AudioServicesPlaySystemSound(soundID)
432 | }
433 | }
434 | }
435 |
436 | public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
437 | guard let tableView = scrollView as? UITableView else {
438 | return
439 | }
440 |
441 | if !decelerate {
442 | alignTableViewToRow(tableView: tableView)
443 | }
444 | }
445 |
446 | public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
447 | guard let tableView = scrollView as? UITableView else {
448 | return
449 | }
450 |
451 | alignTableViewToRow(tableView: tableView)
452 | }
453 |
454 | public func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
455 | guard let tableView = scrollView as? UITableView else {
456 | return
457 | }
458 |
459 | alignTableViewToRow(tableView: tableView)
460 | }
461 |
462 | private func alignTableViewToRow(tableView: UITableView) {
463 | let row = tableView.getRowScroll()
464 |
465 | if tableView == dayTableView {
466 | day = dayRange.lowerBound + row
467 | } else if tableView == monthTableView {
468 | month = monthRange.lowerBound + row
469 | } else if tableView == yearTableView {
470 | year = yearRange.lowerBound + row
471 | }
472 |
473 | delegate?.didSelectDate(day: day, month: month, year: year)
474 | }
475 | }
476 |
477 | // MARK: - Interface Builder properties.
478 | extension DayDatePickerView {
479 | @IBInspectable
480 | public var minYear: NSInteger {
481 | get {
482 | return _minDate?.year ?? MinYear
483 | }
484 | set {
485 | setMinDate(year: newValue, month: _minDate?.month ?? MinMonth, day: _minDate?.day ?? MinDay, animated: true)
486 | }
487 | }
488 |
489 | @IBInspectable
490 | public var minMonth: NSInteger {
491 | get {
492 | return _minDate?.month ?? MinMonth
493 | }
494 | set {
495 | setMinDate(year: _minDate?.year ?? MinYear, month: newValue, day: _minDate?.day ?? MinDay, animated: true)
496 | }
497 | }
498 |
499 | @IBInspectable
500 | public var minDay: NSInteger {
501 | get {
502 | return _minDate?.day ?? MinDay
503 | }
504 | set {
505 | setMinDate(year: _minDate?.year ?? MinYear, month: _minDate?.month ?? MinMonth, day: newValue, animated: true)
506 | }
507 | }
508 |
509 | @IBInspectable
510 | public var maxYear: NSInteger {
511 | get {
512 | return _maxDate?.year ?? MaxYear
513 | }
514 | set {
515 | setMinDate(year: newValue, month: _maxDate?.month ?? MaxMonth, day: _maxDate?.day ?? MaxDay, animated: true)
516 | }
517 | }
518 |
519 | @IBInspectable
520 | public var maxMonth: NSInteger {
521 | get {
522 | return _maxDate?.month ?? MaxMonth
523 | }
524 | set {
525 | setMinDate(year: _maxDate?.year ?? MaxYear, month: newValue, day: _maxDate?.day ?? MaxDay, animated: true)
526 | }
527 | }
528 |
529 | @IBInspectable
530 | public var maxDay: NSInteger {
531 | get {
532 | return _maxDate?.day ?? MaxDay
533 | }
534 | set {
535 | setMinDate(year: _maxDate?.year ?? MaxYear, month: _maxDate?.month ?? MaxMonth, day: newValue, animated: true)
536 | }
537 | }
538 |
539 | @IBInspectable
540 | public var year: NSInteger {
541 | get {
542 | return _date?.year ?? MinYear
543 | }
544 | set {
545 | setDate(year: newValue, month: _date?.month ?? MinMonth, day: _date?.day ?? MinDay, animated: true)
546 | }
547 | }
548 |
549 | @IBInspectable
550 | public var month: NSInteger {
551 | get {
552 | return _date?.month ?? MinMonth
553 | }
554 | set {
555 | setDate(year: _date?.year ?? MinYear, month: newValue, day: _date?.day ?? MinDay, animated: true)
556 | }
557 | }
558 |
559 | @IBInspectable
560 | public var day: NSInteger {
561 | get {
562 | return _date?.day ?? MinDay
563 | }
564 | set {
565 | setDate(year: _date?.year ?? MinYear, month: _date?.month ?? MinMonth, day: newValue, animated: true)
566 | }
567 | }
568 | }
569 |
--------------------------------------------------------------------------------
/Example/DayDatePicker.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1BA5D3026F5AA1EEE9ED5C1E /* Pods_DayDatePicker_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D11F8E469EB11C33A115033 /* Pods_DayDatePicker_Example.framework */; };
11 | 1E408B312028782E00B996E0 /* TimePickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E408B2F2028782E00B996E0 /* TimePickerViewController.swift */; };
12 | 1E408B322028782E00B996E0 /* DayDatePickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E408B302028782E00B996E0 /* DayDatePickerViewController.swift */; };
13 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; };
14 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; };
15 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; };
16 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; };
17 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; };
18 | 751B51B5648754F162CB4D23 /* Pods_DayDatePicker_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D0F308AEB4A04531B1FBBB9 /* Pods_DayDatePicker_Tests.framework */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXContainerItemProxy section */
22 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = {
23 | isa = PBXContainerItemProxy;
24 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */;
25 | proxyType = 1;
26 | remoteGlobalIDString = 607FACCF1AFB9204008FA782;
27 | remoteInfo = DayDatePicker;
28 | };
29 | /* End PBXContainerItemProxy section */
30 |
31 | /* Begin PBXFileReference section */
32 | 1E408B2F2028782E00B996E0 /* TimePickerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimePickerViewController.swift; sourceTree = ""; };
33 | 1E408B302028782E00B996E0 /* DayDatePickerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DayDatePickerViewController.swift; sourceTree = ""; };
34 | 262D93E3B300E96A365CCC48 /* DayDatePicker.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = DayDatePicker.podspec; path = ../DayDatePicker.podspec; sourceTree = ""; };
35 | 2D11F8E469EB11C33A115033 /* Pods_DayDatePicker_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DayDatePicker_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
36 | 2DB0CA1B30CA1CFA29553532 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; };
37 | 447114AC89F85878C1C1CFAE /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; };
38 | 583778CD64742C8AFC66768D /* Pods-DayDatePicker_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DayDatePicker_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DayDatePicker_Example/Pods-DayDatePicker_Example.debug.xcconfig"; sourceTree = ""; };
39 | 5FAE4A69036F13E82C4DC764 /* Pods-DayDatePicker_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DayDatePicker_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-DayDatePicker_Tests/Pods-DayDatePicker_Tests.release.xcconfig"; sourceTree = ""; };
40 | 607FACD01AFB9204008FA782 /* DayDatePicker_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DayDatePicker_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
41 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
42 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
43 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
44 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
45 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
46 | 607FACE51AFB9204008FA782 /* DayDatePicker_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DayDatePicker_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
47 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
48 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; };
49 | 7D0F308AEB4A04531B1FBBB9 /* Pods_DayDatePicker_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DayDatePicker_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
50 | A6C82D1DB805FF53450E32A7 /* Pods-DayDatePicker_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DayDatePicker_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DayDatePicker_Tests/Pods-DayDatePicker_Tests.debug.xcconfig"; sourceTree = ""; };
51 | FF2BFF44C8F23D06CED529B0 /* Pods-DayDatePicker_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DayDatePicker_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-DayDatePicker_Example/Pods-DayDatePicker_Example.release.xcconfig"; sourceTree = ""; };
52 | /* End PBXFileReference section */
53 |
54 | /* Begin PBXFrameworksBuildPhase section */
55 | 607FACCD1AFB9204008FA782 /* Frameworks */ = {
56 | isa = PBXFrameworksBuildPhase;
57 | buildActionMask = 2147483647;
58 | files = (
59 | 1BA5D3026F5AA1EEE9ED5C1E /* Pods_DayDatePicker_Example.framework in Frameworks */,
60 | );
61 | runOnlyForDeploymentPostprocessing = 0;
62 | };
63 | 607FACE21AFB9204008FA782 /* Frameworks */ = {
64 | isa = PBXFrameworksBuildPhase;
65 | buildActionMask = 2147483647;
66 | files = (
67 | 751B51B5648754F162CB4D23 /* Pods_DayDatePicker_Tests.framework in Frameworks */,
68 | );
69 | runOnlyForDeploymentPostprocessing = 0;
70 | };
71 | /* End PBXFrameworksBuildPhase section */
72 |
73 | /* Begin PBXGroup section */
74 | 1A028531237353C6496C776B /* Frameworks */ = {
75 | isa = PBXGroup;
76 | children = (
77 | 2D11F8E469EB11C33A115033 /* Pods_DayDatePicker_Example.framework */,
78 | 7D0F308AEB4A04531B1FBBB9 /* Pods_DayDatePicker_Tests.framework */,
79 | );
80 | name = Frameworks;
81 | sourceTree = "";
82 | };
83 | 607FACC71AFB9204008FA782 = {
84 | isa = PBXGroup;
85 | children = (
86 | 607FACF51AFB993E008FA782 /* Podspec Metadata */,
87 | 607FACD21AFB9204008FA782 /* Example for DayDatePicker */,
88 | 607FACE81AFB9204008FA782 /* Tests */,
89 | 607FACD11AFB9204008FA782 /* Products */,
90 | E19215C4FD7CEB03A4C3D45F /* Pods */,
91 | 1A028531237353C6496C776B /* Frameworks */,
92 | );
93 | sourceTree = "";
94 | };
95 | 607FACD11AFB9204008FA782 /* Products */ = {
96 | isa = PBXGroup;
97 | children = (
98 | 607FACD01AFB9204008FA782 /* DayDatePicker_Example.app */,
99 | 607FACE51AFB9204008FA782 /* DayDatePicker_Tests.xctest */,
100 | );
101 | name = Products;
102 | sourceTree = "";
103 | };
104 | 607FACD21AFB9204008FA782 /* Example for DayDatePicker */ = {
105 | isa = PBXGroup;
106 | children = (
107 | 1E408B302028782E00B996E0 /* DayDatePickerViewController.swift */,
108 | 1E408B2F2028782E00B996E0 /* TimePickerViewController.swift */,
109 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */,
110 | 607FACD91AFB9204008FA782 /* Main.storyboard */,
111 | 607FACDC1AFB9204008FA782 /* Images.xcassets */,
112 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */,
113 | 607FACD31AFB9204008FA782 /* Supporting Files */,
114 | );
115 | name = "Example for DayDatePicker";
116 | path = DayDatePicker;
117 | sourceTree = "";
118 | };
119 | 607FACD31AFB9204008FA782 /* Supporting Files */ = {
120 | isa = PBXGroup;
121 | children = (
122 | 607FACD41AFB9204008FA782 /* Info.plist */,
123 | );
124 | name = "Supporting Files";
125 | sourceTree = "";
126 | };
127 | 607FACE81AFB9204008FA782 /* Tests */ = {
128 | isa = PBXGroup;
129 | children = (
130 | 607FACEB1AFB9204008FA782 /* Tests.swift */,
131 | 607FACE91AFB9204008FA782 /* Supporting Files */,
132 | );
133 | path = Tests;
134 | sourceTree = "";
135 | };
136 | 607FACE91AFB9204008FA782 /* Supporting Files */ = {
137 | isa = PBXGroup;
138 | children = (
139 | 607FACEA1AFB9204008FA782 /* Info.plist */,
140 | );
141 | name = "Supporting Files";
142 | sourceTree = "";
143 | };
144 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = {
145 | isa = PBXGroup;
146 | children = (
147 | 262D93E3B300E96A365CCC48 /* DayDatePicker.podspec */,
148 | 2DB0CA1B30CA1CFA29553532 /* README.md */,
149 | 447114AC89F85878C1C1CFAE /* LICENSE */,
150 | );
151 | name = "Podspec Metadata";
152 | sourceTree = "";
153 | };
154 | E19215C4FD7CEB03A4C3D45F /* Pods */ = {
155 | isa = PBXGroup;
156 | children = (
157 | 583778CD64742C8AFC66768D /* Pods-DayDatePicker_Example.debug.xcconfig */,
158 | FF2BFF44C8F23D06CED529B0 /* Pods-DayDatePicker_Example.release.xcconfig */,
159 | A6C82D1DB805FF53450E32A7 /* Pods-DayDatePicker_Tests.debug.xcconfig */,
160 | 5FAE4A69036F13E82C4DC764 /* Pods-DayDatePicker_Tests.release.xcconfig */,
161 | );
162 | name = Pods;
163 | sourceTree = "";
164 | };
165 | /* End PBXGroup section */
166 |
167 | /* Begin PBXNativeTarget section */
168 | 607FACCF1AFB9204008FA782 /* DayDatePicker_Example */ = {
169 | isa = PBXNativeTarget;
170 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DayDatePicker_Example" */;
171 | buildPhases = (
172 | BAFE27E11D3EFB007C0BC892 /* [CP] Check Pods Manifest.lock */,
173 | 607FACCC1AFB9204008FA782 /* Sources */,
174 | 607FACCD1AFB9204008FA782 /* Frameworks */,
175 | 607FACCE1AFB9204008FA782 /* Resources */,
176 | 9BF538AF3A895E75B29F89E7 /* [CP] Embed Pods Frameworks */,
177 | 9611F9A0A712312F8F505741 /* [CP] Copy Pods Resources */,
178 | );
179 | buildRules = (
180 | );
181 | dependencies = (
182 | );
183 | name = DayDatePicker_Example;
184 | productName = DayDatePicker;
185 | productReference = 607FACD01AFB9204008FA782 /* DayDatePicker_Example.app */;
186 | productType = "com.apple.product-type.application";
187 | };
188 | 607FACE41AFB9204008FA782 /* DayDatePicker_Tests */ = {
189 | isa = PBXNativeTarget;
190 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DayDatePicker_Tests" */;
191 | buildPhases = (
192 | 76113F4C80907BB02338C509 /* [CP] Check Pods Manifest.lock */,
193 | 607FACE11AFB9204008FA782 /* Sources */,
194 | 607FACE21AFB9204008FA782 /* Frameworks */,
195 | 607FACE31AFB9204008FA782 /* Resources */,
196 | C19886854D87E132D8FE98DF /* [CP] Embed Pods Frameworks */,
197 | 6B60E37CAE79E67416FB9F13 /* [CP] Copy Pods Resources */,
198 | );
199 | buildRules = (
200 | );
201 | dependencies = (
202 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */,
203 | );
204 | name = DayDatePicker_Tests;
205 | productName = Tests;
206 | productReference = 607FACE51AFB9204008FA782 /* DayDatePicker_Tests.xctest */;
207 | productType = "com.apple.product-type.bundle.unit-test";
208 | };
209 | /* End PBXNativeTarget section */
210 |
211 | /* Begin PBXProject section */
212 | 607FACC81AFB9204008FA782 /* Project object */ = {
213 | isa = PBXProject;
214 | attributes = {
215 | LastSwiftUpdateCheck = 0830;
216 | LastUpgradeCheck = 0830;
217 | ORGANIZATIONNAME = CocoaPods;
218 | TargetAttributes = {
219 | 607FACCF1AFB9204008FA782 = {
220 | CreatedOnToolsVersion = 6.3.1;
221 | LastSwiftMigration = 0900;
222 | };
223 | 607FACE41AFB9204008FA782 = {
224 | CreatedOnToolsVersion = 6.3.1;
225 | LastSwiftMigration = 0900;
226 | TestTargetID = 607FACCF1AFB9204008FA782;
227 | };
228 | };
229 | };
230 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "DayDatePicker" */;
231 | compatibilityVersion = "Xcode 3.2";
232 | developmentRegion = English;
233 | hasScannedForEncodings = 0;
234 | knownRegions = (
235 | en,
236 | Base,
237 | );
238 | mainGroup = 607FACC71AFB9204008FA782;
239 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */;
240 | projectDirPath = "";
241 | projectRoot = "";
242 | targets = (
243 | 607FACCF1AFB9204008FA782 /* DayDatePicker_Example */,
244 | 607FACE41AFB9204008FA782 /* DayDatePicker_Tests */,
245 | );
246 | };
247 | /* End PBXProject section */
248 |
249 | /* Begin PBXResourcesBuildPhase section */
250 | 607FACCE1AFB9204008FA782 /* Resources */ = {
251 | isa = PBXResourcesBuildPhase;
252 | buildActionMask = 2147483647;
253 | files = (
254 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */,
255 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */,
256 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */,
257 | );
258 | runOnlyForDeploymentPostprocessing = 0;
259 | };
260 | 607FACE31AFB9204008FA782 /* Resources */ = {
261 | isa = PBXResourcesBuildPhase;
262 | buildActionMask = 2147483647;
263 | files = (
264 | );
265 | runOnlyForDeploymentPostprocessing = 0;
266 | };
267 | /* End PBXResourcesBuildPhase section */
268 |
269 | /* Begin PBXShellScriptBuildPhase section */
270 | 6B60E37CAE79E67416FB9F13 /* [CP] Copy Pods Resources */ = {
271 | isa = PBXShellScriptBuildPhase;
272 | buildActionMask = 2147483647;
273 | files = (
274 | );
275 | inputPaths = (
276 | );
277 | name = "[CP] Copy Pods Resources";
278 | outputPaths = (
279 | );
280 | runOnlyForDeploymentPostprocessing = 0;
281 | shellPath = /bin/sh;
282 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DayDatePicker_Tests/Pods-DayDatePicker_Tests-resources.sh\"\n";
283 | showEnvVarsInLog = 0;
284 | };
285 | 76113F4C80907BB02338C509 /* [CP] Check Pods Manifest.lock */ = {
286 | isa = PBXShellScriptBuildPhase;
287 | buildActionMask = 2147483647;
288 | files = (
289 | );
290 | inputPaths = (
291 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
292 | "${PODS_ROOT}/Manifest.lock",
293 | );
294 | name = "[CP] Check Pods Manifest.lock";
295 | outputPaths = (
296 | "$(DERIVED_FILE_DIR)/Pods-DayDatePicker_Tests-checkManifestLockResult.txt",
297 | );
298 | runOnlyForDeploymentPostprocessing = 0;
299 | shellPath = /bin/sh;
300 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
301 | showEnvVarsInLog = 0;
302 | };
303 | 9611F9A0A712312F8F505741 /* [CP] Copy Pods Resources */ = {
304 | isa = PBXShellScriptBuildPhase;
305 | buildActionMask = 2147483647;
306 | files = (
307 | );
308 | inputPaths = (
309 | );
310 | name = "[CP] Copy Pods Resources";
311 | outputPaths = (
312 | );
313 | runOnlyForDeploymentPostprocessing = 0;
314 | shellPath = /bin/sh;
315 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DayDatePicker_Example/Pods-DayDatePicker_Example-resources.sh\"\n";
316 | showEnvVarsInLog = 0;
317 | };
318 | 9BF538AF3A895E75B29F89E7 /* [CP] Embed Pods Frameworks */ = {
319 | isa = PBXShellScriptBuildPhase;
320 | buildActionMask = 2147483647;
321 | files = (
322 | );
323 | inputPaths = (
324 | "${SRCROOT}/Pods/Target Support Files/Pods-DayDatePicker_Example/Pods-DayDatePicker_Example-frameworks.sh",
325 | "${BUILT_PRODUCTS_DIR}/DayDatePicker/DayDatePicker.framework",
326 | );
327 | name = "[CP] Embed Pods Frameworks";
328 | outputPaths = (
329 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DayDatePicker.framework",
330 | );
331 | runOnlyForDeploymentPostprocessing = 0;
332 | shellPath = /bin/sh;
333 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DayDatePicker_Example/Pods-DayDatePicker_Example-frameworks.sh\"\n";
334 | showEnvVarsInLog = 0;
335 | };
336 | BAFE27E11D3EFB007C0BC892 /* [CP] Check Pods Manifest.lock */ = {
337 | isa = PBXShellScriptBuildPhase;
338 | buildActionMask = 2147483647;
339 | files = (
340 | );
341 | inputPaths = (
342 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
343 | "${PODS_ROOT}/Manifest.lock",
344 | );
345 | name = "[CP] Check Pods Manifest.lock";
346 | outputPaths = (
347 | "$(DERIVED_FILE_DIR)/Pods-DayDatePicker_Example-checkManifestLockResult.txt",
348 | );
349 | runOnlyForDeploymentPostprocessing = 0;
350 | shellPath = /bin/sh;
351 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
352 | showEnvVarsInLog = 0;
353 | };
354 | C19886854D87E132D8FE98DF /* [CP] Embed Pods Frameworks */ = {
355 | isa = PBXShellScriptBuildPhase;
356 | buildActionMask = 2147483647;
357 | files = (
358 | );
359 | inputPaths = (
360 | "${SRCROOT}/Pods/Target Support Files/Pods-DayDatePicker_Tests/Pods-DayDatePicker_Tests-frameworks.sh",
361 | "${BUILT_PRODUCTS_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework",
362 | );
363 | name = "[CP] Embed Pods Frameworks";
364 | outputPaths = (
365 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSnapshotTestCase.framework",
366 | );
367 | runOnlyForDeploymentPostprocessing = 0;
368 | shellPath = /bin/sh;
369 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DayDatePicker_Tests/Pods-DayDatePicker_Tests-frameworks.sh\"\n";
370 | showEnvVarsInLog = 0;
371 | };
372 | /* End PBXShellScriptBuildPhase section */
373 |
374 | /* Begin PBXSourcesBuildPhase section */
375 | 607FACCC1AFB9204008FA782 /* Sources */ = {
376 | isa = PBXSourcesBuildPhase;
377 | buildActionMask = 2147483647;
378 | files = (
379 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */,
380 | 1E408B322028782E00B996E0 /* DayDatePickerViewController.swift in Sources */,
381 | 1E408B312028782E00B996E0 /* TimePickerViewController.swift in Sources */,
382 | );
383 | runOnlyForDeploymentPostprocessing = 0;
384 | };
385 | 607FACE11AFB9204008FA782 /* Sources */ = {
386 | isa = PBXSourcesBuildPhase;
387 | buildActionMask = 2147483647;
388 | files = (
389 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */,
390 | );
391 | runOnlyForDeploymentPostprocessing = 0;
392 | };
393 | /* End PBXSourcesBuildPhase section */
394 |
395 | /* Begin PBXTargetDependency section */
396 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = {
397 | isa = PBXTargetDependency;
398 | target = 607FACCF1AFB9204008FA782 /* DayDatePicker_Example */;
399 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */;
400 | };
401 | /* End PBXTargetDependency section */
402 |
403 | /* Begin PBXVariantGroup section */
404 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = {
405 | isa = PBXVariantGroup;
406 | children = (
407 | 607FACDA1AFB9204008FA782 /* Base */,
408 | );
409 | name = Main.storyboard;
410 | sourceTree = "";
411 | };
412 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = {
413 | isa = PBXVariantGroup;
414 | children = (
415 | 607FACDF1AFB9204008FA782 /* Base */,
416 | );
417 | name = LaunchScreen.xib;
418 | sourceTree = "";
419 | };
420 | /* End PBXVariantGroup section */
421 |
422 | /* Begin XCBuildConfiguration section */
423 | 607FACED1AFB9204008FA782 /* Debug */ = {
424 | isa = XCBuildConfiguration;
425 | buildSettings = {
426 | ALWAYS_SEARCH_USER_PATHS = NO;
427 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
428 | CLANG_CXX_LIBRARY = "libc++";
429 | CLANG_ENABLE_MODULES = YES;
430 | CLANG_ENABLE_OBJC_ARC = YES;
431 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
432 | CLANG_WARN_BOOL_CONVERSION = YES;
433 | CLANG_WARN_COMMA = YES;
434 | CLANG_WARN_CONSTANT_CONVERSION = YES;
435 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
436 | CLANG_WARN_EMPTY_BODY = YES;
437 | CLANG_WARN_ENUM_CONVERSION = YES;
438 | CLANG_WARN_INFINITE_RECURSION = YES;
439 | CLANG_WARN_INT_CONVERSION = YES;
440 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
441 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
442 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
443 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
444 | CLANG_WARN_STRICT_PROTOTYPES = YES;
445 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
446 | CLANG_WARN_UNREACHABLE_CODE = YES;
447 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
448 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
449 | COPY_PHASE_STRIP = NO;
450 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
451 | ENABLE_STRICT_OBJC_MSGSEND = YES;
452 | ENABLE_TESTABILITY = YES;
453 | GCC_C_LANGUAGE_STANDARD = gnu99;
454 | GCC_DYNAMIC_NO_PIC = NO;
455 | GCC_NO_COMMON_BLOCKS = YES;
456 | GCC_OPTIMIZATION_LEVEL = 0;
457 | GCC_PREPROCESSOR_DEFINITIONS = (
458 | "DEBUG=1",
459 | "$(inherited)",
460 | );
461 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
462 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
463 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
464 | GCC_WARN_UNDECLARED_SELECTOR = YES;
465 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
466 | GCC_WARN_UNUSED_FUNCTION = YES;
467 | GCC_WARN_UNUSED_VARIABLE = YES;
468 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
469 | MTL_ENABLE_DEBUG_INFO = YES;
470 | ONLY_ACTIVE_ARCH = YES;
471 | SDKROOT = iphoneos;
472 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
473 | };
474 | name = Debug;
475 | };
476 | 607FACEE1AFB9204008FA782 /* Release */ = {
477 | isa = XCBuildConfiguration;
478 | buildSettings = {
479 | ALWAYS_SEARCH_USER_PATHS = NO;
480 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
481 | CLANG_CXX_LIBRARY = "libc++";
482 | CLANG_ENABLE_MODULES = YES;
483 | CLANG_ENABLE_OBJC_ARC = YES;
484 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
485 | CLANG_WARN_BOOL_CONVERSION = YES;
486 | CLANG_WARN_COMMA = YES;
487 | CLANG_WARN_CONSTANT_CONVERSION = YES;
488 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
489 | CLANG_WARN_EMPTY_BODY = YES;
490 | CLANG_WARN_ENUM_CONVERSION = YES;
491 | CLANG_WARN_INFINITE_RECURSION = YES;
492 | CLANG_WARN_INT_CONVERSION = YES;
493 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
494 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
495 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
496 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
497 | CLANG_WARN_STRICT_PROTOTYPES = YES;
498 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
499 | CLANG_WARN_UNREACHABLE_CODE = YES;
500 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
501 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
502 | COPY_PHASE_STRIP = NO;
503 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
504 | ENABLE_NS_ASSERTIONS = NO;
505 | ENABLE_STRICT_OBJC_MSGSEND = YES;
506 | GCC_C_LANGUAGE_STANDARD = gnu99;
507 | GCC_NO_COMMON_BLOCKS = YES;
508 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
509 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
510 | GCC_WARN_UNDECLARED_SELECTOR = YES;
511 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
512 | GCC_WARN_UNUSED_FUNCTION = YES;
513 | GCC_WARN_UNUSED_VARIABLE = YES;
514 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
515 | MTL_ENABLE_DEBUG_INFO = NO;
516 | SDKROOT = iphoneos;
517 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
518 | VALIDATE_PRODUCT = YES;
519 | };
520 | name = Release;
521 | };
522 | 607FACF01AFB9204008FA782 /* Debug */ = {
523 | isa = XCBuildConfiguration;
524 | baseConfigurationReference = 583778CD64742C8AFC66768D /* Pods-DayDatePicker_Example.debug.xcconfig */;
525 | buildSettings = {
526 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
527 | INFOPLIST_FILE = DayDatePicker/Info.plist;
528 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
529 | MODULE_NAME = ExampleApp;
530 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
531 | PRODUCT_NAME = "$(TARGET_NAME)";
532 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
533 | SWIFT_VERSION = 4.0;
534 | };
535 | name = Debug;
536 | };
537 | 607FACF11AFB9204008FA782 /* Release */ = {
538 | isa = XCBuildConfiguration;
539 | baseConfigurationReference = FF2BFF44C8F23D06CED529B0 /* Pods-DayDatePicker_Example.release.xcconfig */;
540 | buildSettings = {
541 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
542 | INFOPLIST_FILE = DayDatePicker/Info.plist;
543 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
544 | MODULE_NAME = ExampleApp;
545 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
546 | PRODUCT_NAME = "$(TARGET_NAME)";
547 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
548 | SWIFT_VERSION = 4.0;
549 | };
550 | name = Release;
551 | };
552 | 607FACF31AFB9204008FA782 /* Debug */ = {
553 | isa = XCBuildConfiguration;
554 | baseConfigurationReference = A6C82D1DB805FF53450E32A7 /* Pods-DayDatePicker_Tests.debug.xcconfig */;
555 | buildSettings = {
556 | FRAMEWORK_SEARCH_PATHS = "$(inherited)";
557 | GCC_PREPROCESSOR_DEFINITIONS = (
558 | "DEBUG=1",
559 | "$(inherited)",
560 | );
561 | INFOPLIST_FILE = Tests/Info.plist;
562 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
563 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
564 | PRODUCT_NAME = "$(TARGET_NAME)";
565 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
566 | SWIFT_VERSION = 4.0;
567 | };
568 | name = Debug;
569 | };
570 | 607FACF41AFB9204008FA782 /* Release */ = {
571 | isa = XCBuildConfiguration;
572 | baseConfigurationReference = 5FAE4A69036F13E82C4DC764 /* Pods-DayDatePicker_Tests.release.xcconfig */;
573 | buildSettings = {
574 | FRAMEWORK_SEARCH_PATHS = "$(inherited)";
575 | INFOPLIST_FILE = Tests/Info.plist;
576 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
577 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
578 | PRODUCT_NAME = "$(TARGET_NAME)";
579 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
580 | SWIFT_VERSION = 4.0;
581 | };
582 | name = Release;
583 | };
584 | /* End XCBuildConfiguration section */
585 |
586 | /* Begin XCConfigurationList section */
587 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "DayDatePicker" */ = {
588 | isa = XCConfigurationList;
589 | buildConfigurations = (
590 | 607FACED1AFB9204008FA782 /* Debug */,
591 | 607FACEE1AFB9204008FA782 /* Release */,
592 | );
593 | defaultConfigurationIsVisible = 0;
594 | defaultConfigurationName = Release;
595 | };
596 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DayDatePicker_Example" */ = {
597 | isa = XCConfigurationList;
598 | buildConfigurations = (
599 | 607FACF01AFB9204008FA782 /* Debug */,
600 | 607FACF11AFB9204008FA782 /* Release */,
601 | );
602 | defaultConfigurationIsVisible = 0;
603 | defaultConfigurationName = Release;
604 | };
605 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DayDatePicker_Tests" */ = {
606 | isa = XCConfigurationList;
607 | buildConfigurations = (
608 | 607FACF31AFB9204008FA782 /* Debug */,
609 | 607FACF41AFB9204008FA782 /* Release */,
610 | );
611 | defaultConfigurationIsVisible = 0;
612 | defaultConfigurationName = Release;
613 | };
614 | /* End XCConfigurationList section */
615 | };
616 | rootObject = 607FACC81AFB9204008FA782 /* Project object */;
617 | }
618 |
--------------------------------------------------------------------------------
/Example/DayDatePicker/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
--------------------------------------------------------------------------------