Merged PR 210708: MSDateTimePicker Presentation API with DateTime support
Introduces a new `MSDateTimePicker` class to manage and handle presenting multiple Date or Time related pickers and coordinating between them. Includes a refactor of `MSDatePicker` into `MSDatePickerController`, leaving one Controller class for each type of picker. Also includes some miscellaneous SwiftLint update fixes. ![Screen Shot 2018-12-07 at 4.27.44 PM.png](https://onedrive.visualstudio.com/4dcbf0bc-c3cd-49c8-a7c3-ec1924691d9b/_apis/git/repositories/93ac71ee-b53a-4fc6-a8c4-d46a80d4ca39/pullRequests/210708/attachments/Screen%20Shot%202018-12-07%20at%204.27.44%20PM.png) ![Screen Shot 2018-12-07 at 4.27.48 PM.png](https://onedrive.visualstudio.com/4dcbf0bc-c3cd-49c8-a7c3-ec1924691d9b/_apis/git/repositories/93ac71ee-b53a-4fc6-a8c4-d46a80d4ca39/pullRequests/210708/attachments/Screen%20Shot%202018-12-07%20at%204.27.48%20PM.png) Related work items: #665501
This commit is contained in:
Родитель
522ae9c053
Коммит
198f12b1cb
|
@ -50,9 +50,6 @@ whitelist_rules:
|
||||||
# Computed read-only properties and subscripts should avoid using the get keyword.
|
# Computed read-only properties and subscripts should avoid using the get keyword.
|
||||||
- implicit_getter
|
- implicit_getter
|
||||||
|
|
||||||
# If defer is at the end of its parent scope, it will be executed right where it is anyway.
|
|
||||||
- inert_defer
|
|
||||||
|
|
||||||
# Files should not contain leading whitespace.
|
# Files should not contain leading whitespace.
|
||||||
- leading_whitespace
|
- leading_whitespace
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import UIKit
|
||||||
let demos: [(title: String, controllerClass: UIViewController.Type)] = [
|
let demos: [(title: String, controllerClass: UIViewController.Type)] = [
|
||||||
("MSAvatarView", MSAvatarViewDemoController.self),
|
("MSAvatarView", MSAvatarViewDemoController.self),
|
||||||
("MSBadgeView", MSBadgeViewDemoController.self),
|
("MSBadgeView", MSBadgeViewDemoController.self),
|
||||||
("MSDatePicker", MSDatePickerDemoController.self),
|
("MSDateTimePicker", MSDateTimePickerDemoController.self),
|
||||||
("MSDrawerController", MSDrawerDemoController.self),
|
("MSDrawerController", MSDrawerDemoController.self),
|
||||||
("MSLabel", MSLabelDemoController.self),
|
("MSLabel", MSLabelDemoController.self),
|
||||||
("MSPersonaListView", MSPersonaListViewDemoController.self),
|
("MSPersonaListView", MSPersonaListViewDemoController.self),
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import OfficeUIFabric
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class MSDatePickerDemoController: DemoController {
|
|
||||||
private var dateLabel: MSLabel?
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
dateLabel = MSLabel(style: .headline, colorStyle: .primary)
|
|
||||||
dateLabel?.text = "No date selected"
|
|
||||||
container.addArrangedSubview(dateLabel!)
|
|
||||||
container.addArrangedSubview(createButton(title: "Show date picker", action: #selector(presentDatePicker)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func presentDatePicker() {
|
|
||||||
let datePicker = MSDatePicker(initialDate: Date())
|
|
||||||
datePicker.delegate = self
|
|
||||||
datePicker.present(from: self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - MSDatePickerDemoController: MSDatePickerDelegate
|
|
||||||
|
|
||||||
extension MSDatePickerDemoController: MSDatePickerDelegate {
|
|
||||||
func datePicker(_ datePicker: MSDatePicker, didPickDate date: Date) {
|
|
||||||
dateLabel?.text = String.dateString(from: date, compactness: .longDaynameDayMonthYear)
|
|
||||||
dismiss(animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
//
|
||||||
|
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import OfficeUIFabric
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class MSDateTimePickerDemoController: DemoController {
|
||||||
|
private let dateLabel = MSLabel(style: .headline, colorStyle: .primary)
|
||||||
|
private let dateTimePicker = MSDateTimePicker()
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
dateTimePicker.delegate = self
|
||||||
|
dateLabel.text = "No date selected"
|
||||||
|
container.addArrangedSubview(dateLabel)
|
||||||
|
container.addArrangedSubview(createButton(title: "Show date picker", action: #selector(presentDatePicker)))
|
||||||
|
container.addArrangedSubview(createButton(title: "Show date time picker", action: #selector(presentDateTimePicker)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func presentDatePicker() {
|
||||||
|
dateTimePicker.present(from: self, with: .date)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func presentDateTimePicker() {
|
||||||
|
dateTimePicker.present(from: self, with: .dateTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - MSDateTimePickerDemoController: MSDatePickerDelegate
|
||||||
|
|
||||||
|
extension MSDateTimePickerDemoController: MSDateTimePickerDelegate {
|
||||||
|
func dateTimePicker(_ dateTimePicker: MSDateTimePicker, didPickDate date: Date) {
|
||||||
|
var compactness = MSDateStringCompactness.longDaynameDayMonthYear
|
||||||
|
if dateTimePicker.mode == .dateTime {
|
||||||
|
compactness = .longDaynameDayMonthHoursColumnsMinutes
|
||||||
|
}
|
||||||
|
dateLabel.text = String.dateString(from: date, compactness: compactness)
|
||||||
|
dateTimePicker.dismiss()
|
||||||
|
}
|
||||||
|
}
|
|
@ -55,13 +55,12 @@
|
||||||
FD256C5B2183B90B00EC9588 /* MSDatePickerSelectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD256C5A2183B90B00EC9588 /* MSDatePickerSelectionManager.swift */; };
|
FD256C5B2183B90B00EC9588 /* MSDatePickerSelectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD256C5A2183B90B00EC9588 /* MSDatePickerSelectionManager.swift */; };
|
||||||
FD36F1A9216C0A6900CECBC6 /* MSCardPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA1AF9221487225001AE720 /* MSCardPresentationController.swift */; };
|
FD36F1A9216C0A6900CECBC6 /* MSCardPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA1AF9221487225001AE720 /* MSCardPresentationController.swift */; };
|
||||||
FD4F2A1B2148937100C437D6 /* MSPageCardPresenterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD4F2A1A2148937100C437D6 /* MSPageCardPresenterController.swift */; };
|
FD4F2A1B2148937100C437D6 /* MSPageCardPresenterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD4F2A1A2148937100C437D6 /* MSPageCardPresenterController.swift */; };
|
||||||
FD4F2A1E214ADBCF00C437D6 /* MSDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD4F2A1D214ADBCF00C437D6 /* MSDatePicker.swift */; };
|
|
||||||
FD4F2A20214AE20400C437D6 /* MSDatePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD4F2A1F214AE20400C437D6 /* MSDatePickerController.swift */; };
|
FD4F2A20214AE20400C437D6 /* MSDatePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD4F2A1F214AE20400C437D6 /* MSDatePickerController.swift */; };
|
||||||
FD56FD92219123FE0023C7EA /* MSDateTimePickerViewComponentTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9758082191118E00B67319 /* MSDateTimePickerViewComponentTableView.swift */; };
|
FD56FD92219123FE0023C7EA /* MSDateTimePickerViewComponentTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9758082191118E00B67319 /* MSDateTimePickerViewComponentTableView.swift */; };
|
||||||
FD56FD94219128BF0023C7EA /* MSDateTimePickerViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9758072191118E00B67319 /* MSDateTimePickerViewDataSource.swift */; };
|
FD56FD94219128BF0023C7EA /* MSDateTimePickerViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9758072191118E00B67319 /* MSDateTimePickerViewDataSource.swift */; };
|
||||||
FD56FD95219131430023C7EA /* MSDateTimePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9758062191118D00B67319 /* MSDateTimePickerView.swift */; };
|
FD56FD95219131430023C7EA /* MSDateTimePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9758062191118D00B67319 /* MSDateTimePickerView.swift */; };
|
||||||
FD56FD962192754B0023C7EA /* MSDateTimePickerViewComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9758092191118E00B67319 /* MSDateTimePickerViewComponent.swift */; };
|
FD56FD962192754B0023C7EA /* MSDateTimePickerViewComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9758092191118E00B67319 /* MSDateTimePickerViewComponent.swift */; };
|
||||||
FD56FD9A2194E50D0023C7EA /* MSDateTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5ADBF32190CDC80005A9AF /* MSDateTimePicker.swift */; };
|
FD56FD9A2194E50D0023C7EA /* MSDateTimePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5ADBF32190CDC80005A9AF /* MSDateTimePickerController.swift */; };
|
||||||
FD599D0221348439008845EE /* MSCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD599D0121348439008845EE /* MSCalendarView.swift */; };
|
FD599D0221348439008845EE /* MSCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD599D0121348439008845EE /* MSCalendarView.swift */; };
|
||||||
FD599D062134A682008845EE /* AccessibleViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD599D052134A682008845EE /* AccessibleViewDelegate.swift */; };
|
FD599D062134A682008845EE /* AccessibleViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD599D052134A682008845EE /* AccessibleViewDelegate.swift */; };
|
||||||
FD599D082134AB0E008845EE /* MSCalendarViewWeekdayHeadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD599D072134AB0E008845EE /* MSCalendarViewWeekdayHeadingView.swift */; };
|
FD599D082134AB0E008845EE /* MSCalendarViewWeekdayHeadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD599D072134AB0E008845EE /* MSCalendarViewWeekdayHeadingView.swift */; };
|
||||||
|
@ -77,6 +76,7 @@
|
||||||
FD777529219E3F6C00033D58 /* MSDayOfMonth.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD777528219E3F6C00033D58 /* MSDayOfMonth.swift */; };
|
FD777529219E3F6C00033D58 /* MSDayOfMonth.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD777528219E3F6C00033D58 /* MSDayOfMonth.swift */; };
|
||||||
FD77752B219E455A00033D58 /* MSAccessibilityContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD77752A219E455A00033D58 /* MSAccessibilityContainerView.swift */; };
|
FD77752B219E455A00033D58 /* MSAccessibilityContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD77752A219E455A00033D58 /* MSAccessibilityContainerView.swift */; };
|
||||||
FD77752D219E62E100033D58 /* MSDateTimePickerViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD77752C219E62E100033D58 /* MSDateTimePickerViewLayout.swift */; };
|
FD77752D219E62E100033D58 /* MSDateTimePickerViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD77752C219E62E100033D58 /* MSDateTimePickerViewLayout.swift */; };
|
||||||
|
FD77753021A490BA00033D58 /* MSDateTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD77752F21A490BA00033D58 /* MSDateTimePicker.swift */; };
|
||||||
FD97580F2191118E00B67319 /* MSDateTimePickerViewComponentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD97580A2191118E00B67319 /* MSDateTimePickerViewComponentCell.swift */; };
|
FD97580F2191118E00B67319 /* MSDateTimePickerViewComponentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD97580A2191118E00B67319 /* MSDateTimePickerViewComponentCell.swift */; };
|
||||||
FD9A5C872179464F00D224D9 /* DateComponents+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9A5C862179464F00D224D9 /* DateComponents+Extensions.swift */; };
|
FD9A5C872179464F00D224D9 /* DateComponents+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9A5C862179464F00D224D9 /* DateComponents+Extensions.swift */; };
|
||||||
FDA1AF8C21484625001AE720 /* MSBlurringView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA1AF8B21484625001AE720 /* MSBlurringView.swift */; };
|
FDA1AF8C21484625001AE720 /* MSBlurringView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA1AF8B21484625001AE720 /* MSBlurringView.swift */; };
|
||||||
|
@ -152,14 +152,13 @@
|
||||||
FD0D29D52151A3D700E8655E /* MSCardPresenterNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSCardPresenterNavigationController.swift; sourceTree = "<group>"; };
|
FD0D29D52151A3D700E8655E /* MSCardPresenterNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSCardPresenterNavigationController.swift; sourceTree = "<group>"; };
|
||||||
FD256C5A2183B90B00EC9588 /* MSDatePickerSelectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSDatePickerSelectionManager.swift; sourceTree = "<group>"; };
|
FD256C5A2183B90B00EC9588 /* MSDatePickerSelectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSDatePickerSelectionManager.swift; sourceTree = "<group>"; };
|
||||||
FD4F2A1A2148937100C437D6 /* MSPageCardPresenterController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSPageCardPresenterController.swift; sourceTree = "<group>"; };
|
FD4F2A1A2148937100C437D6 /* MSPageCardPresenterController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSPageCardPresenterController.swift; sourceTree = "<group>"; };
|
||||||
FD4F2A1D214ADBCF00C437D6 /* MSDatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSDatePicker.swift; sourceTree = "<group>"; };
|
|
||||||
FD4F2A1F214AE20400C437D6 /* MSDatePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSDatePickerController.swift; sourceTree = "<group>"; };
|
FD4F2A1F214AE20400C437D6 /* MSDatePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSDatePickerController.swift; sourceTree = "<group>"; };
|
||||||
FD599D0121348439008845EE /* MSCalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSCalendarView.swift; sourceTree = "<group>"; };
|
FD599D0121348439008845EE /* MSCalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSCalendarView.swift; sourceTree = "<group>"; };
|
||||||
FD599D052134A682008845EE /* AccessibleViewDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessibleViewDelegate.swift; sourceTree = "<group>"; };
|
FD599D052134A682008845EE /* AccessibleViewDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessibleViewDelegate.swift; sourceTree = "<group>"; };
|
||||||
FD599D072134AB0E008845EE /* MSCalendarViewWeekdayHeadingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSCalendarViewWeekdayHeadingView.swift; sourceTree = "<group>"; };
|
FD599D072134AB0E008845EE /* MSCalendarViewWeekdayHeadingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSCalendarViewWeekdayHeadingView.swift; sourceTree = "<group>"; };
|
||||||
FD599D092134AB15008845EE /* MSCalendarViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSCalendarViewLayout.swift; sourceTree = "<group>"; };
|
FD599D092134AB15008845EE /* MSCalendarViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSCalendarViewLayout.swift; sourceTree = "<group>"; };
|
||||||
FD599D0B2134AB1E008845EE /* MSCalendarViewDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSCalendarViewDataSource.swift; sourceTree = "<group>"; };
|
FD599D0B2134AB1E008845EE /* MSCalendarViewDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSCalendarViewDataSource.swift; sourceTree = "<group>"; };
|
||||||
FD5ADBF32190CDC80005A9AF /* MSDateTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSDateTimePicker.swift; sourceTree = "<group>"; };
|
FD5ADBF32190CDC80005A9AF /* MSDateTimePickerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSDateTimePickerController.swift; sourceTree = "<group>"; };
|
||||||
FD5BBE3A214B2F44008964B4 /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = "<group>"; };
|
FD5BBE3A214B2F44008964B4 /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
FD5BBE3E214C68F8008964B4 /* UINavigationBar+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationBar+Extensions.swift"; sourceTree = "<group>"; };
|
FD5BBE3E214C68F8008964B4 /* UINavigationBar+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationBar+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
FD5BBE40214C6AF3008964B4 /* MSTwoLinesTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSTwoLinesTitleView.swift; sourceTree = "<group>"; };
|
FD5BBE40214C6AF3008964B4 /* MSTwoLinesTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSTwoLinesTitleView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -169,6 +168,7 @@
|
||||||
FD777528219E3F6C00033D58 /* MSDayOfMonth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSDayOfMonth.swift; sourceTree = "<group>"; };
|
FD777528219E3F6C00033D58 /* MSDayOfMonth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSDayOfMonth.swift; sourceTree = "<group>"; };
|
||||||
FD77752A219E455A00033D58 /* MSAccessibilityContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSAccessibilityContainerView.swift; sourceTree = "<group>"; };
|
FD77752A219E455A00033D58 /* MSAccessibilityContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSAccessibilityContainerView.swift; sourceTree = "<group>"; };
|
||||||
FD77752C219E62E100033D58 /* MSDateTimePickerViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSDateTimePickerViewLayout.swift; sourceTree = "<group>"; };
|
FD77752C219E62E100033D58 /* MSDateTimePickerViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSDateTimePickerViewLayout.swift; sourceTree = "<group>"; };
|
||||||
|
FD77752F21A490BA00033D58 /* MSDateTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSDateTimePicker.swift; sourceTree = "<group>"; };
|
||||||
FD9758062191118D00B67319 /* MSDateTimePickerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSDateTimePickerView.swift; sourceTree = "<group>"; };
|
FD9758062191118D00B67319 /* MSDateTimePickerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSDateTimePickerView.swift; sourceTree = "<group>"; };
|
||||||
FD9758072191118E00B67319 /* MSDateTimePickerViewDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSDateTimePickerViewDataSource.swift; sourceTree = "<group>"; };
|
FD9758072191118E00B67319 /* MSDateTimePickerViewDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSDateTimePickerViewDataSource.swift; sourceTree = "<group>"; };
|
||||||
FD9758082191118E00B67319 /* MSDateTimePickerViewComponentTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSDateTimePickerViewComponentTableView.swift; sourceTree = "<group>"; };
|
FD9758082191118E00B67319 /* MSDateTimePickerViewComponentTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSDateTimePickerViewComponentTableView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -399,7 +399,6 @@
|
||||||
FD4F2A1C214ADBBF00C437D6 /* Date Picker */ = {
|
FD4F2A1C214ADBBF00C437D6 /* Date Picker */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
FD4F2A1D214ADBCF00C437D6 /* MSDatePicker.swift */,
|
|
||||||
FD4F2A1F214AE20400C437D6 /* MSDatePickerController.swift */,
|
FD4F2A1F214AE20400C437D6 /* MSDatePickerController.swift */,
|
||||||
FD256C5A2183B90B00EC9588 /* MSDatePickerSelectionManager.swift */,
|
FD256C5A2183B90B00EC9588 /* MSDatePickerSelectionManager.swift */,
|
||||||
);
|
);
|
||||||
|
@ -411,6 +410,7 @@
|
||||||
children = (
|
children = (
|
||||||
FD4F2A1C214ADBBF00C437D6 /* Date Picker */,
|
FD4F2A1C214ADBBF00C437D6 /* Date Picker */,
|
||||||
FD5C8C2A2190C3470063562C /* Date Time Picker */,
|
FD5C8C2A2190C3470063562C /* Date Time Picker */,
|
||||||
|
FD77752F21A490BA00033D58 /* MSDateTimePicker.swift */,
|
||||||
);
|
);
|
||||||
path = "Date Time Pickers";
|
path = "Date Time Pickers";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -418,7 +418,7 @@
|
||||||
FD5C8C2A2190C3470063562C /* Date Time Picker */ = {
|
FD5C8C2A2190C3470063562C /* Date Time Picker */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
FD5ADBF32190CDC80005A9AF /* MSDateTimePicker.swift */,
|
FD5ADBF32190CDC80005A9AF /* MSDateTimePickerController.swift */,
|
||||||
FD97580521910FE800B67319 /* Views */,
|
FD97580521910FE800B67319 /* Views */,
|
||||||
);
|
);
|
||||||
path = "Date Time Picker";
|
path = "Date Time Picker";
|
||||||
|
@ -640,7 +640,6 @@
|
||||||
A5B87B04211E22B70038C37C /* MSDimmingView.swift in Sources */,
|
A5B87B04211E22B70038C37C /* MSDimmingView.swift in Sources */,
|
||||||
A5961F9F218A256B00E2A506 /* MSPopupMenuItem.swift in Sources */,
|
A5961F9F218A256B00E2A506 /* MSPopupMenuItem.swift in Sources */,
|
||||||
B4E782C321793AB200A7DFCE /* MSActivityIndicatorCell.swift in Sources */,
|
B4E782C321793AB200A7DFCE /* MSActivityIndicatorCell.swift in Sources */,
|
||||||
FD4F2A1E214ADBCF00C437D6 /* MSDatePicker.swift in Sources */,
|
|
||||||
FD599D0A2134AB15008845EE /* MSCalendarViewLayout.swift in Sources */,
|
FD599D0A2134AB15008845EE /* MSCalendarViewLayout.swift in Sources */,
|
||||||
FDD454EE21405B390006E84E /* MSDotView.swift in Sources */,
|
FDD454EE21405B390006E84E /* MSDotView.swift in Sources */,
|
||||||
FD256C5B2183B90B00EC9588 /* MSDatePickerSelectionManager.swift in Sources */,
|
FD256C5B2183B90B00EC9588 /* MSDatePickerSelectionManager.swift in Sources */,
|
||||||
|
@ -657,10 +656,11 @@
|
||||||
FD5BBE43214C73CE008964B4 /* MSEasyTapButton.swift in Sources */,
|
FD5BBE43214C73CE008964B4 /* MSEasyTapButton.swift in Sources */,
|
||||||
FD77752B219E455A00033D58 /* MSAccessibilityContainerView.swift in Sources */,
|
FD77752B219E455A00033D58 /* MSAccessibilityContainerView.swift in Sources */,
|
||||||
FDF41ED92141A02200EC527C /* MSCalendarConfiguration.swift in Sources */,
|
FDF41ED92141A02200EC527C /* MSCalendarConfiguration.swift in Sources */,
|
||||||
|
FD77753021A490BA00033D58 /* MSDateTimePicker.swift in Sources */,
|
||||||
FD7254E92147059D002F4069 /* Calendar+Extensions.swift in Sources */,
|
FD7254E92147059D002F4069 /* Calendar+Extensions.swift in Sources */,
|
||||||
A559BB7E212B6D100055E107 /* String+Extension.swift in Sources */,
|
A559BB7E212B6D100055E107 /* String+Extension.swift in Sources */,
|
||||||
A5961FA5218A260500E2A506 /* MSPopupMenuSectionHeaderView.swift in Sources */,
|
A5961FA5218A260500E2A506 /* MSPopupMenuSectionHeaderView.swift in Sources */,
|
||||||
FD56FD9A2194E50D0023C7EA /* MSDateTimePicker.swift in Sources */,
|
FD56FD9A2194E50D0023C7EA /* MSDateTimePickerController.swift in Sources */,
|
||||||
B46D3F9D215985AC0029772C /* MSPersonaListView.swift in Sources */,
|
B46D3F9D215985AC0029772C /* MSPersonaListView.swift in Sources */,
|
||||||
A5DCA76421224026005F4CB7 /* MSSeparator.swift in Sources */,
|
A5DCA76421224026005F4CB7 /* MSSeparator.swift in Sources */,
|
||||||
FD599D062134A682008845EE /* AccessibleViewDelegate.swift in Sources */,
|
FD599D062134A682008845EE /* AccessibleViewDelegate.swift in Sources */,
|
||||||
|
|
|
@ -28,8 +28,6 @@ class MSCalendarViewWeekdayHeadingView: UIView {
|
||||||
private var firstWeekday: Int?
|
private var firstWeekday: Int?
|
||||||
private let headerStyle: MSDatePickerHeaderStyle
|
private let headerStyle: MSDatePickerHeaderStyle
|
||||||
|
|
||||||
// TODO: Remove this exception when SwiftLint rule false positive is fixed
|
|
||||||
// swiftlint:disable:next explicit_type_interface
|
|
||||||
private var headingLabels = [UILabel]()
|
private var headingLabels = [UILabel]()
|
||||||
|
|
||||||
init(headerStyle: MSDatePickerHeaderStyle) {
|
init(headerStyle: MSDatePickerHeaderStyle) {
|
||||||
|
|
|
@ -1,203 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
// MARK: MSDatePickerHeaderStyle
|
|
||||||
|
|
||||||
@objc public enum MSDatePickerHeaderStyle: Int {
|
|
||||||
case light
|
|
||||||
case dark
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - MSDatePickerDelegate
|
|
||||||
|
|
||||||
/// Allows a class to be notified when a user confirms their selected date
|
|
||||||
public protocol MSDatePickerDelegate: class {
|
|
||||||
func datePicker(_ datePicker: MSDatePicker, didPickDate date: Date)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - MSDatePicker
|
|
||||||
|
|
||||||
/// A view controller that can present itself modally to display a calendar used for picking a date, with a navigation bar with title.
|
|
||||||
open class MSDatePicker: UIViewController {
|
|
||||||
private struct Constants {
|
|
||||||
// TODO: Make title button width dynamic
|
|
||||||
static let titleButtonWidth: CGFloat = 160
|
|
||||||
static let preloadAvailabilityDaysOffset: Int = 30
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The currently selected whole date. Automatically changes to start of day when set.
|
|
||||||
open var date: Date {
|
|
||||||
get {
|
|
||||||
return contentController.startDate
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
let startDate = newValue.startOfDay
|
|
||||||
contentController.setup(startDate: startDate, endDate: startDate.adding(hours: 23, minutes: 59))
|
|
||||||
updateNavigationBar()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public weak var delegate: MSDatePickerDelegate?
|
|
||||||
|
|
||||||
private var titleView: MSTwoLinesTitleView!
|
|
||||||
private let subtitle: String?
|
|
||||||
private var contentController: MSDatePickerController!
|
|
||||||
|
|
||||||
// TODO: Add availability back in? - contactAvailabilitySummaryDataSource: ContactAvailabilitySummaryDataSource?,
|
|
||||||
|
|
||||||
/// Creates and sets up a calendar-style date picker, with a specified date shown first.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - initialDate: A date object on the day chosen to be selected.
|
|
||||||
/// - overrideSubtitle: An optional string describing an optional subtitle for this date picker.
|
|
||||||
public init(initialDate: Date, subtitle: String? = nil) {
|
|
||||||
self.subtitle = subtitle
|
|
||||||
|
|
||||||
super.init(nibName: nil, bundle: nil)
|
|
||||||
|
|
||||||
let selectedDate = initialDate.startOfDay
|
|
||||||
initContentController(startDate: selectedDate, endDate: selectedDate.adding(hours: 23, minutes: 59), selectionMode: .start)
|
|
||||||
initTitleView()
|
|
||||||
}
|
|
||||||
|
|
||||||
public required init?(coder aDecoder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Presents this date picker over the selected view controller, using a card modal style.
|
|
||||||
///
|
|
||||||
/// - Parameter presentingViewController: The view controller the date picker will be presented on top of
|
|
||||||
public func present(from presentingViewController: UIViewController) {
|
|
||||||
if UIAccessibilityIsVoiceOverRunning() {
|
|
||||||
let dateTimePicker = MSDateTimePicker(date: date, showsTime: false)
|
|
||||||
dateTimePicker.delegate = self
|
|
||||||
dateTimePicker.present(from: presentingViewController)
|
|
||||||
}
|
|
||||||
|
|
||||||
let navController = MSCardPresenterNavigationController(rootViewController: self)
|
|
||||||
let pageCardPresenterVC = MSPageCardPresenterController(viewControllers: [navController], startingIndex: 0)
|
|
||||||
|
|
||||||
pageCardPresenterVC.onDismiss = {
|
|
||||||
presentingViewController.dismiss(animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
presentingViewController.present(pageCardPresenterVC, animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
addChildController(contentController)
|
|
||||||
initNavigationBar()
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func viewDidLayoutSubviews() {
|
|
||||||
super.viewDidLayoutSubviews()
|
|
||||||
|
|
||||||
updateTitleFrame()
|
|
||||||
|
|
||||||
contentController.view.frame = view.bounds
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func viewWillAppear(_ animated: Bool) {
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
|
|
||||||
// Hide default bottom border of navigation bar
|
|
||||||
navigationController?.navigationBar.hideBottomBorder()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Create components
|
|
||||||
|
|
||||||
private func initTitleView() {
|
|
||||||
titleView = MSTwoLinesTitleView()
|
|
||||||
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTitleButtonTapped))
|
|
||||||
titleView.addGestureRecognizer(tapRecognizer)
|
|
||||||
|
|
||||||
updateNavigationBar()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func initContentController(startDate: Date, endDate: Date, selectionMode: MSDatePickerSelectionManager.SelectionMode) {
|
|
||||||
contentController = MSDatePickerController(
|
|
||||||
startDate: startDate,
|
|
||||||
endDate: endDate,
|
|
||||||
selectionMode: selectionMode
|
|
||||||
)
|
|
||||||
contentController.delegate = self
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Add components to hierarchy
|
|
||||||
|
|
||||||
private func initNavigationBar() {
|
|
||||||
if let image = UIImage.staticImageNamed("checkmark-blue-25x25"),
|
|
||||||
let landscapeImage = UIImage.staticImageNamed("checkmark-blue-thin-20x20") {
|
|
||||||
navigationItem.rightBarButtonItem = UIBarButtonItem(image: image, landscapeImagePhone: landscapeImage, style: .plain, target: self, action: #selector(handleDidTapDone))
|
|
||||||
}
|
|
||||||
if let image = UIImage.staticImageNamed("back-25x25") {
|
|
||||||
navigationItem.leftBarButtonItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(handleDidTapBack))
|
|
||||||
navigationItem.leftBarButtonItem?.tintColor = MSColors.buttonImage
|
|
||||||
}
|
|
||||||
navigationItem.titleView = titleView
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Update components
|
|
||||||
|
|
||||||
private func updateNavigationBar() {
|
|
||||||
let title = String.dateString(from: contentController.focusDate, compactness: .shortDaynameShortMonthnameDay)
|
|
||||||
titleView.setup(title: title, subtitle: subtitle)
|
|
||||||
updateTitleFrame()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updateTitleFrame() {
|
|
||||||
// Title
|
|
||||||
if let navigationController = navigationController {
|
|
||||||
titleView.frame = CGRect(
|
|
||||||
x: 0.0,
|
|
||||||
y: 0.0,
|
|
||||||
width: Constants.titleButtonWidth,
|
|
||||||
height: navigationController.navigationBar.height
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Handlers
|
|
||||||
|
|
||||||
@objc private func handleTitleButtonTapped() {
|
|
||||||
contentController.scrollToStartDate(animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func handleDidTapDone() {
|
|
||||||
delegate?.datePicker(self, didPickDate: date)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func handleDidTapBack() {
|
|
||||||
parent?.dismiss(animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - MSDatePicker: MSDatePickerControllerDelegate
|
|
||||||
|
|
||||||
extension MSDatePicker: MSDatePickerControllerDelegate {
|
|
||||||
func datePickerController(_ datePickerController: MSDatePickerController, didSelectDate: Date) {
|
|
||||||
updateNavigationBar()
|
|
||||||
// TODO: Add delegate call for notifying date did change
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - MSDatePicker: MSDateTimePickerDelegate
|
|
||||||
|
|
||||||
extension MSDatePicker: MSDateTimePickerDelegate {
|
|
||||||
func dateTimePicker(_ dateTimePicker: MSDateTimePicker, didPickDate date: Date) {
|
|
||||||
delegate?.datePicker(self, didPickDate: date)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - MSDatePicker: MSCardPresentable
|
|
||||||
|
|
||||||
extension MSDatePicker: MSCardPresentable {
|
|
||||||
public func idealSize() -> CGSize {
|
|
||||||
return contentController.idealSize()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,10 +4,11 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
// MARK: MSDatePickerControllerDelegate
|
// MARK: MSDatePickerHeaderStyle
|
||||||
|
|
||||||
protocol MSDatePickerControllerDelegate: class {
|
@objc enum MSDatePickerHeaderStyle: Int {
|
||||||
func datePickerController(_ datePickerController: MSDatePickerController, didSelectDate date: Date)
|
case light
|
||||||
|
case dark
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MSDatePickerController
|
// MARK: - MSDatePickerController
|
||||||
|
@ -16,10 +17,25 @@ protocol MSDatePickerControllerDelegate: class {
|
||||||
* Represents a date picker, that enables the user to scroll through years vertically week by week.
|
* Represents a date picker, that enables the user to scroll through years vertically week by week.
|
||||||
* The user can select a date or a range of dates.
|
* The user can select a date or a range of dates.
|
||||||
*/
|
*/
|
||||||
|
class MSDatePickerController: UIViewController, DateTimePicker {
|
||||||
class MSDatePickerController: UIViewController {
|
|
||||||
private struct Constants {
|
private struct Constants {
|
||||||
static let idealWidth: CGFloat = 343
|
static let idealWidth: CGFloat = 343
|
||||||
|
// TODO: Make title button width dynamic
|
||||||
|
static let titleButtonWidth: CGFloat = 160
|
||||||
|
static let preloadAvailabilityDaysOffset: Int = 30
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary date property for single date selection and DateTimePicker conformance. Will remove when MSDateSelectable is refactored to include start and end date.
|
||||||
|
/// The currently selected whole date. Automatically changes to start of day when set.
|
||||||
|
var date: Date {
|
||||||
|
get {
|
||||||
|
return startDate
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
let startDate = newValue.startOfDay
|
||||||
|
setup(startDate: startDate, endDate: startDate.adding(hours: 23, minutes: 59))
|
||||||
|
updateNavigationBar()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstWeekday: Int = Calendar.current.firstWeekday
|
var firstWeekday: Int = Calendar.current.firstWeekday
|
||||||
|
@ -27,8 +43,6 @@ class MSDatePickerController: UIViewController {
|
||||||
var startDate: Date { return selectionManager.selectedDates.startDate }
|
var startDate: Date { return selectionManager.selectedDates.startDate }
|
||||||
var endDate: Date { return selectionManager.selectedDates.endDate }
|
var endDate: Date { return selectionManager.selectedDates.endDate }
|
||||||
|
|
||||||
weak var delegate: MSDatePickerControllerDelegate?
|
|
||||||
|
|
||||||
var visibleDates: (startDate: Date, endDate: Date)? {
|
var visibleDates: (startDate: Date, endDate: Date)? {
|
||||||
let contentOffset = calendarView.collectionView.contentOffset
|
let contentOffset = calendarView.collectionView.contentOffset
|
||||||
return visibleDates(at: CGPoint(x: 0, y: contentOffset.y))
|
return visibleDates(at: CGPoint(x: 0, y: contentOffset.y))
|
||||||
|
@ -38,6 +52,11 @@ class MSDatePickerController: UIViewController {
|
||||||
return selectionManager.selectionMode == .start ? startDate : endDate
|
return selectionManager.selectionMode == .start ? startDate : endDate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
weak var delegate: DateTimePickerDelegate?
|
||||||
|
|
||||||
|
private var titleView: MSTwoLinesTitleView!
|
||||||
|
private let subtitle: String?
|
||||||
|
|
||||||
private var monthOverlayIsShown: Bool = false
|
private var monthOverlayIsShown: Bool = false
|
||||||
private var reloadDataAfterOverlayIsNeeded: Bool = false
|
private var reloadDataAfterOverlayIsNeeded: Bool = false
|
||||||
|
|
||||||
|
@ -45,20 +64,33 @@ class MSDatePickerController: UIViewController {
|
||||||
private let calendarView = MSCalendarView()
|
private let calendarView = MSCalendarView()
|
||||||
private var calendarViewDataSource: MSCalendarViewDataSource!
|
private var calendarViewDataSource: MSCalendarViewDataSource!
|
||||||
|
|
||||||
// TODO: Add availability back in? availabilityDataSource: MeetingCalendarDatePickerAvailabilityDataSource?
|
// TODO: Add availability back in? - contactAvailabilitySummaryDataSource: ContactAvailabilitySummaryDataSource?,
|
||||||
init(startDate: Date, endDate: Date, selectionMode: MSDatePickerSelectionManager.SelectionMode) {
|
|
||||||
|
/// Creates and sets up a calendar-style date picker, with a specified date shown first.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - startDate: A date object for the start day or day/time to be initially selected. Until range implemented, changes to start of day.
|
||||||
|
/// - endDate: An optional date object for an end day or day/time to be initially selected. Until range implemented, ignored.
|
||||||
|
/// - selectionMode: The side (start or end) of the current range to be selected on this picker.
|
||||||
|
/// - subtitle: An optional string describing an optional subtitle for this date picker.
|
||||||
|
init(startDate: Date, endDate: Date? = nil, selectionMode: MSDatePickerSelectionManager.SelectionMode = .start, subtitle: String? = nil) {
|
||||||
|
self.subtitle = subtitle
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
|
||||||
calendarViewDataSource = MSCalendarViewDataSource(styleDataSource: self)
|
calendarViewDataSource = MSCalendarViewDataSource(styleDataSource: self)
|
||||||
|
|
||||||
|
let startDate = startDate.startOfDay
|
||||||
selectionManager = MSDatePickerSelectionManager(
|
selectionManager = MSDatePickerSelectionManager(
|
||||||
dataSource: calendarViewDataSource,
|
dataSource: calendarViewDataSource,
|
||||||
startDate: startDate,
|
startDate: startDate,
|
||||||
endDate: endDate,
|
endDate: startDate.adding(hours: 23, minutes: 59),
|
||||||
selectionMode: selectionMode
|
selectionMode: selectionMode
|
||||||
)
|
)
|
||||||
|
|
||||||
|
initTitleView()
|
||||||
}
|
}
|
||||||
|
|
||||||
public required init?(coder aDecoder: NSCoder) {
|
required init?(coder aDecoder: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +111,7 @@ class MSDatePickerController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
calendarView.weekdayHeadingView.setup(horizontalSizeClass: traitCollection.horizontalSizeClass, firstWeekday: firstWeekday)
|
calendarView.weekdayHeadingView.setup(horizontalSizeClass: traitCollection.horizontalSizeClass, firstWeekday: firstWeekday)
|
||||||
|
@ -99,27 +131,65 @@ class MSDatePickerController: UIViewController {
|
||||||
calendarView.collectionViewLayout.delegate = self
|
calendarView.collectionViewLayout.delegate = self
|
||||||
|
|
||||||
view.addSubview(calendarView)
|
view.addSubview(calendarView)
|
||||||
|
|
||||||
|
initNavigationBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
scrollToStartDate(animated: false)
|
scrollToStartDate(animated: false)
|
||||||
|
|
||||||
|
// Hide default bottom border of navigation bar
|
||||||
|
navigationController?.navigationBar.hideBottomBorder()
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func viewWillLayoutSubviews() {
|
override func viewWillLayoutSubviews() {
|
||||||
super.viewWillLayoutSubviews()
|
super.viewWillLayoutSubviews()
|
||||||
|
|
||||||
calendarView.frame = view.bounds
|
calendarView.frame = view.bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
func scrollToStartDate(animated: Bool) {
|
private func initTitleView() {
|
||||||
|
titleView = MSTwoLinesTitleView()
|
||||||
|
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTitleButtonTapped))
|
||||||
|
titleView.addGestureRecognizer(tapRecognizer)
|
||||||
|
|
||||||
|
updateNavigationBar()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func initNavigationBar() {
|
||||||
|
if let image = UIImage.staticImageNamed("checkmark-blue-25x25"),
|
||||||
|
let landscapeImage = UIImage.staticImageNamed("checkmark-blue-thin-20x20") {
|
||||||
|
navigationItem.rightBarButtonItem = UIBarButtonItem(image: image, landscapeImagePhone: landscapeImage, style: .plain, target: self, action: #selector(handleDidTapDone))
|
||||||
|
}
|
||||||
|
navigationItem.titleView = titleView
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateNavigationBar() {
|
||||||
|
let title = String.dateString(from: focusDate, compactness: .shortDaynameShortMonthnameDay)
|
||||||
|
titleView.setup(title: title, subtitle: subtitle)
|
||||||
|
updateTitleFrame()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateTitleFrame() {
|
||||||
|
if let navigationController = navigationController {
|
||||||
|
titleView.frame = CGRect(
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
width: Constants.titleButtonWidth,
|
||||||
|
height: navigationController.navigationBar.height
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func scrollToStartDate(animated: Bool) {
|
||||||
let targetIndexPath = IndexPath(item: 0, section: max(selectionManager.startDateIndexPath.section - 1, 0))
|
let targetIndexPath = IndexPath(item: 0, section: max(selectionManager.startDateIndexPath.section - 1, 0))
|
||||||
calendarView.collectionView.scrollToItem(at: targetIndexPath, at: [.top], animated: animated)
|
calendarView.collectionView.scrollToItem(at: targetIndexPath, at: [.top], animated: animated)
|
||||||
// TODO: Notify of visible date?
|
// TODO: Notify of visible date?
|
||||||
}
|
}
|
||||||
|
|
||||||
func setNeedsReloadAvailability() {
|
private func setNeedsReloadAvailability() {
|
||||||
reloadDataAfterOverlayIsNeeded = true
|
reloadDataAfterOverlayIsNeeded = true
|
||||||
reloadDataAfterOverlayHiddenIfNeeded()
|
reloadDataAfterOverlayHiddenIfNeeded()
|
||||||
}
|
}
|
||||||
|
@ -151,12 +221,22 @@ class MSDatePickerController: UIViewController {
|
||||||
let endDate = calendarViewDataSource.dayEnd(forDayAt: endIndexPath)
|
let endDate = calendarViewDataSource.dayEnd(forDayAt: endIndexPath)
|
||||||
return (startDate: startDate, endDate: endDate)
|
return (startDate: startDate, endDate: endDate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Handlers
|
||||||
|
|
||||||
|
@objc private func handleTitleButtonTapped() {
|
||||||
|
scrollToStartDate(animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func handleDidTapDone() {
|
||||||
|
delegate?.dateTimePicker(self, didPickDate: date)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MSDatePickerController: UICollectionViewDelegate
|
// MARK: - MSDatePickerController: UICollectionViewDelegate
|
||||||
|
|
||||||
extension MSDatePickerController: UICollectionViewDelegate {
|
extension MSDatePickerController: UICollectionViewDelegate {
|
||||||
public func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath) {
|
func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath) {
|
||||||
guard let monthBannerView = view as? MSCalendarViewMonthBannerView else {
|
guard let monthBannerView = view as? MSCalendarViewMonthBannerView else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -164,7 +244,7 @@ extension MSDatePickerController: UICollectionViewDelegate {
|
||||||
monthBannerView.setVisible(monthOverlayIsShown, animated: false)
|
monthBannerView.setVisible(monthOverlayIsShown, animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
||||||
guard let dayCell = cell as? MSCalendarViewDayCell else {
|
guard let dayCell = cell as? MSCalendarViewDayCell else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -174,11 +254,11 @@ extension MSDatePickerController: UICollectionViewDelegate {
|
||||||
updateSelectionOfVisibleCells()
|
updateSelectionOfVisibleCells()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
didTapItem(at: indexPath)
|
didTapItem(at: indexPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
|
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
|
||||||
didTapItem(at: indexPath)
|
didTapItem(at: indexPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +268,9 @@ extension MSDatePickerController: UICollectionViewDelegate {
|
||||||
updateSelectionOfVisibleCells()
|
updateSelectionOfVisibleCells()
|
||||||
|
|
||||||
let (startDate, _) = selectionManager.selectedDates
|
let (startDate, _) = selectionManager.selectedDates
|
||||||
delegate?.datePickerController(self, didSelectDate: startDate)
|
delegate?.dateTimePicker(self, didSelectDate: startDate)
|
||||||
|
|
||||||
|
updateNavigationBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateSelectionOfVisibleCells() {
|
private func updateSelectionOfVisibleCells() {
|
||||||
|
@ -248,7 +330,7 @@ extension MSDatePickerController: UIScrollViewDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||||
changeMonthOverlayVisibility(false)
|
changeMonthOverlayVisibility(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,28 +4,28 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
// MARK: MSDateTimePickerDelegate
|
// MARK: MSDateTimePickerController
|
||||||
|
|
||||||
@objc protocol MSDateTimePickerDelegate: class {
|
class MSDateTimePickerController: UIViewController, DateTimePicker {
|
||||||
@objc func dateTimePicker(_ dateTimePicker: MSDateTimePicker, didPickDate date: Date)
|
|
||||||
@objc optional func dateTimePicker(_ dateTimePicker: MSDateTimePicker, didSelectDate date: Date)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - MSDateTimePicker
|
|
||||||
|
|
||||||
class MSDateTimePicker: UIViewController {
|
|
||||||
private struct Constants {
|
private struct Constants {
|
||||||
static let idealRowCount: Int = 7
|
static let idealRowCount: Int = 7
|
||||||
static let idealWidth: CGFloat = 320
|
static let idealWidth: CGFloat = 320
|
||||||
|
static let titleButtonWidth: CGFloat = 160
|
||||||
}
|
}
|
||||||
|
|
||||||
var mode: MSDateTimePickerViewMode { return dateTimePickerView.mode }
|
var mode: MSDateTimePickerViewMode { return dateTimePickerView.mode }
|
||||||
|
|
||||||
weak var delegate: MSDateTimePickerDelegate?
|
var date: Date {
|
||||||
|
didSet {
|
||||||
|
dateTimePickerView.setDate(date, animated: false)
|
||||||
|
updateNavigationBar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var date: Date
|
weak var delegate: DateTimePickerDelegate?
|
||||||
|
|
||||||
private let dateTimePickerView: MSDateTimePickerView
|
private let dateTimePickerView: MSDateTimePickerView
|
||||||
|
private let titleView = MSTwoLinesTitleView()
|
||||||
|
|
||||||
// TODO: Add availability back in? - contactAvailabilitySummaryDataSource: ContactAvailabilitySummaryDataSource?,
|
// TODO: Add availability back in? - contactAvailabilitySummaryDataSource: ContactAvailabilitySummaryDataSource?,
|
||||||
init(date: Date, showsTime: Bool = true) {
|
init(date: Date, showsTime: Bool = true) {
|
||||||
|
@ -38,32 +38,19 @@ class MSDateTimePicker: UIViewController {
|
||||||
|
|
||||||
dateTimePickerView.addTarget(self, action: #selector(handleDidSelectDate(_:)), for: .valueChanged)
|
dateTimePickerView.addTarget(self, action: #selector(handleDidSelectDate(_:)), for: .valueChanged)
|
||||||
|
|
||||||
initNavigationBar()
|
updateNavigationBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
required init?(coder aDecoder: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Presents this date time picker over the selected view controller, using a card modal style.
|
|
||||||
///
|
|
||||||
/// - Parameter presentingViewController: The view controller the date picker will be presented on top of
|
|
||||||
func present(from presentingViewController: UIViewController) {
|
|
||||||
let navController = MSCardPresenterNavigationController(rootViewController: self)
|
|
||||||
let pageCardPresenterVC = MSPageCardPresenterController(viewControllers: [navController], startingIndex: 0)
|
|
||||||
|
|
||||||
pageCardPresenterVC.onDismiss = {
|
|
||||||
self.dismiss(accept: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
presentingViewController.present(pageCardPresenterVC, animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
view.backgroundColor = MSColors.background
|
view.backgroundColor = MSColors.background
|
||||||
|
|
||||||
view.addSubview(dateTimePickerView)
|
view.addSubview(dateTimePickerView)
|
||||||
|
initNavigationBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidLayoutSubviews() {
|
override func viewDidLayoutSubviews() {
|
||||||
|
@ -83,9 +70,23 @@ class MSDateTimePicker: UIViewController {
|
||||||
let landscapeImage = UIImage.staticImageNamed("checkmark-blue-thin-20x20") {
|
let landscapeImage = UIImage.staticImageNamed("checkmark-blue-thin-20x20") {
|
||||||
navigationItem.rightBarButtonItem = UIBarButtonItem(image: image, landscapeImagePhone: landscapeImage, style: .plain, target: self, action: #selector(handleDidTapDone))
|
navigationItem.rightBarButtonItem = UIBarButtonItem(image: image, landscapeImagePhone: landscapeImage, style: .plain, target: self, action: #selector(handleDidTapDone))
|
||||||
}
|
}
|
||||||
if let image = UIImage.staticImageNamed("back-25x25") {
|
navigationItem.titleView = titleView
|
||||||
navigationItem.leftBarButtonItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(handleDidTapBack))
|
}
|
||||||
navigationItem.leftBarButtonItem?.tintColor = MSColors.buttonImage
|
|
||||||
|
private func updateNavigationBar() {
|
||||||
|
let title = String.dateString(from: date, compactness: .shortDaynameShortMonthnameDay)
|
||||||
|
titleView.setup(title: title)
|
||||||
|
updateTitleFrame()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateTitleFrame() {
|
||||||
|
if let navigationController = navigationController {
|
||||||
|
titleView.frame = CGRect(
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
width: Constants.titleButtonWidth,
|
||||||
|
height: navigationController.navigationBar.height
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,21 +99,17 @@ class MSDateTimePicker: UIViewController {
|
||||||
|
|
||||||
@objc private func handleDidSelectDate(_ datePicker: MSDateTimePickerView) {
|
@objc private func handleDidSelectDate(_ datePicker: MSDateTimePickerView) {
|
||||||
date = datePicker.date
|
date = datePicker.date
|
||||||
delegate?.dateTimePicker?(self, didSelectDate: date)
|
delegate?.dateTimePicker(self, didSelectDate: date)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func handleDidTapDone(_ item: UIBarButtonItem) {
|
@objc private func handleDidTapDone(_ item: UIBarButtonItem) {
|
||||||
dismiss(accept: true)
|
dismiss(accept: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func handleDidTapBack(_ item: UIBarButtonItem) {
|
|
||||||
dismiss(accept: false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MSDateTimePicker: MSCardPresentable
|
// MARK: - MSDateTimePickerController: MSCardPresentable
|
||||||
|
|
||||||
extension MSDateTimePicker: MSCardPresentable {
|
extension MSDateTimePickerController: MSCardPresentable {
|
||||||
func idealSize() -> CGSize {
|
func idealSize() -> CGSize {
|
||||||
let height = MSDateTimePickerViewLayout.height(forRowCount: Constants.idealRowCount)
|
let height = MSDateTimePickerViewLayout.height(forRowCount: Constants.idealRowCount)
|
||||||
return CGSize(width: Constants.idealWidth, height: height)
|
return CGSize(width: Constants.idealWidth, height: height)
|
|
@ -0,0 +1,117 @@
|
||||||
|
//
|
||||||
|
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// MARK: MSDateTimePickerMode
|
||||||
|
|
||||||
|
@objc public enum MSDateTimePickerMode: Int {
|
||||||
|
case date
|
||||||
|
case dateTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - MSDateTimePickerDelegate
|
||||||
|
|
||||||
|
@objc public protocol MSDateTimePickerDelegate: class {
|
||||||
|
/// Allows a class to be notified when a user confirms their selected date
|
||||||
|
func dateTimePicker(_ dateTimePicker: MSDateTimePicker, didPickDate date: Date)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - DateTimePicker
|
||||||
|
|
||||||
|
protocol DateTimePicker: class {
|
||||||
|
var date: Date { get set }
|
||||||
|
var delegate: DateTimePickerDelegate? { get set }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - DateTimePickerDelegate
|
||||||
|
|
||||||
|
protocol DateTimePickerDelegate: class {
|
||||||
|
func dateTimePicker(_ dateTimePicker: DateTimePicker, didPickDate date: Date)
|
||||||
|
func dateTimePicker(_ dateTimePicker: DateTimePicker, didSelectDate date: Date)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - MSDateTimePicker
|
||||||
|
|
||||||
|
/// Manages the presentation and coordination of different date and time pickers
|
||||||
|
public class MSDateTimePicker: NSObject {
|
||||||
|
public private(set) var mode: MSDateTimePickerMode?
|
||||||
|
@objc public weak var delegate: MSDateTimePickerDelegate?
|
||||||
|
|
||||||
|
private var presentingController: UIViewController?
|
||||||
|
private var presentedPickers: [DateTimePicker]?
|
||||||
|
|
||||||
|
/// Presents a picker or set of pickers from a `presentingController` depending on the mode selected. Also handles accessibility replacement presentation.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - presentingController: The view controller that is presenting these pickers
|
||||||
|
/// - mode: Enum describing which mode of pickers should be presented
|
||||||
|
/// - date: The initial date selected on the presented pickers
|
||||||
|
@objc public func present(from presentingController: UIViewController, with mode: MSDateTimePickerMode, for date: Date = Date()) {
|
||||||
|
self.presentingController = presentingController
|
||||||
|
if UIAccessibilityIsVoiceOverRunning() {
|
||||||
|
presentDateTimePickerForAccessibility(initialDate: date, showsTime: mode == .dateTime)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.mode = mode
|
||||||
|
switch mode {
|
||||||
|
case .date:
|
||||||
|
presentDatePicker(initialDate: date)
|
||||||
|
case .dateTime:
|
||||||
|
presentDateTimePicker(initialDate: date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc public func dismiss() {
|
||||||
|
presentingController?.dismiss(animated: true)
|
||||||
|
mode = nil
|
||||||
|
presentedPickers = nil
|
||||||
|
presentingController = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentDatePicker(initialDate: Date) {
|
||||||
|
let datePicker = MSDatePickerController(startDate: initialDate)
|
||||||
|
present([datePicker])
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentDateTimePicker(initialDate: Date) {
|
||||||
|
let datePicker = MSDatePickerController(startDate: initialDate)
|
||||||
|
let dateTimePicker = MSDateTimePickerController(date: initialDate, showsTime: true)
|
||||||
|
present([datePicker, dateTimePicker])
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentDateTimePickerForAccessibility(initialDate: Date, showsTime: Bool) {
|
||||||
|
let dateTimePicker = MSDateTimePickerController(date: initialDate, showsTime: showsTime)
|
||||||
|
present([dateTimePicker])
|
||||||
|
}
|
||||||
|
|
||||||
|
private func present(_ pickers: [DateTimePicker]) {
|
||||||
|
pickers.forEach { $0.delegate = self }
|
||||||
|
presentedPickers = pickers
|
||||||
|
|
||||||
|
let viewControllers = pickers.map { MSCardPresenterNavigationController(rootViewController: $0 as! UIViewController) }
|
||||||
|
let pageCardPresenter = MSPageCardPresenterController(viewControllers: viewControllers, startingIndex: 0)
|
||||||
|
pageCardPresenter.onDismiss = {
|
||||||
|
self.dismiss()
|
||||||
|
}
|
||||||
|
presentingController?.present(pageCardPresenter, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - MSDateTimePicker: DateTimePickerDelegate
|
||||||
|
|
||||||
|
extension MSDateTimePicker: DateTimePickerDelegate {
|
||||||
|
func dateTimePicker(_ dateTimePicker: DateTimePicker, didPickDate date: Date) {
|
||||||
|
delegate?.dateTimePicker(self, didPickDate: date)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dateTimePicker(_ dateTimePicker: DateTimePicker, didSelectDate date: Date) {
|
||||||
|
guard let presentedPickers = presentedPickers else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for picker in presentedPickers where picker !== dateTimePicker {
|
||||||
|
picker.date = date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,6 @@ import Foundation
|
||||||
|
|
||||||
public extension Calendar {
|
public extension Calendar {
|
||||||
private static let sharedAutoUpdatingCalendar: Calendar = .autoupdatingCurrent
|
private static let sharedAutoUpdatingCalendar: Calendar = .autoupdatingCurrent
|
||||||
// TODO: Remove this exception when SwiftLint rule false positive is fixed
|
|
||||||
// swiftlint:disable:next explicit_type_interface
|
|
||||||
private static var sharedTimeZoneCalendars = [Calendar]()
|
private static var sharedTimeZoneCalendars = [Calendar]()
|
||||||
|
|
||||||
static func sharedCalendarWithTimeZone(_ timeZone: TimeZone?) -> Calendar {
|
static func sharedCalendarWithTimeZone(_ timeZone: TimeZone?) -> Calendar {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче