├── 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 | ![Screenshot 1](https://github.com/hughbe/DayDatePicker/blob/master/resources/screenshots/1.png "Screenshot 1") 6 | ![Screenshot 2](https://github.com/hughbe/DayDatePicker/blob/master/resources/screenshots/2.png "Screenshot 2") 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 | --------------------------------------------------------------------------------