Merged PR 260439: Fix DatePickerController dismiss bug

Added a new optional delegate method to `MSDateTimePickerDelegate` that allows a consumer to turn off modal auto-dismiss when the done button is pressed. This allows the user to validate or do asynchronous work before either dismissing themselves via `MSDateTimePicker.dismiss()`, or not dismiss altogether, effectively canceling a dismiss. If the delegate method is not implemented, auto-dismiss when done is pressed defaults to on.

Also added an example of this to the demo with a switch to turn on said validation.
This commit is contained in:
Will Richman 2019-04-25 21:17:26 +00:00
Родитель 0287fbb3e2
Коммит ea6b5773a9
7 изменённых файлов: 77 добавлений и 22 удалений

Просмотреть файл

@ -10,6 +10,9 @@ class MSDateTimePickerDemoController: DemoController {
private let dateLabel = MSLabel(style: .headline)
private let dateTimePicker = MSDateTimePicker()
private let validationSwitch = UISwitch()
private var isValidating: Bool { return validationSwitch.isOn }
private var startDate: Date?
private var endDate: Date?
@ -18,15 +21,31 @@ class MSDateTimePickerDemoController: DemoController {
dateTimePicker.delegate = self
dateLabel.text = "No date selected"
dateLabel.adjustsFontSizeToFitWidth = true
container.addArrangedSubview(dateLabel)
container.addArrangedSubview(createButton(title: "Show date picker", action: #selector(presentDatePicker)))
container.addArrangedSubview(createButton(title: "Show date time picker", action: #selector(presentDateTimePicker)))
container.addArrangedSubview(createButton(title: "Show date range picker", action: #selector(presentDateRangePicker)))
container.addArrangedSubview(createButton(title: "Show date time range picker", action: #selector(presentDateTimeRangePicker)))
container.addArrangedSubview(UIView())
container.addArrangedSubview(createValidationUI())
container.addArrangedSubview(createButton(title: "Reset selected dates", action: #selector(resetDates)))
}
func createValidationUI() -> UIStackView {
let validationRow = UIStackView()
validationRow.axis = .horizontal
validationRow.alignment = .center
validationRow.distribution = .equalSpacing
let validationLabel = MSLabel(style: .subhead, colorStyle: .regular)
validationLabel.text = "Validate for date in future"
validationRow.addArrangedSubview(validationLabel)
validationRow.addArrangedSubview(validationSwitch)
return validationRow
}
@objc func presentDatePicker() {
dateTimePicker.present(from: self, with: .date, startDate: startDate ?? Date())
}
@ -61,7 +80,9 @@ extension MSDateTimePickerDemoController: MSDateTimePickerDelegate {
guard let mode = dateTimePicker.mode else {
fatalError("Received delegate call when mode = nil")
}
self.startDate = startDate
let compactness: MSDateStringCompactness
if mode.singleSelection {
if mode.includesTime {
@ -79,7 +100,16 @@ extension MSDateTimePickerDemoController: MSDateTimePickerDelegate {
}
dateLabel.text = String.dateString(from: startDate, compactness: compactness) + " - " + String.dateString(from: endDate, compactness: compactness)
}
}
dateTimePicker.dismiss()
func dateTimePicker(_ dateTimePicker: MSDateTimePicker, shouldEndPickingStartDate startDate: Date, endDate: Date) -> Bool {
if isValidating && startDate.timeIntervalSinceNow < 0 {
// Start date is in the past, cancel selection and don't dismiss the picker
let alert = UIAlertController(title: "Error", message: "Can't pick a date in the past", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
presentedViewController?.present(alert, animated: true)
return false
}
return true
}
}

Просмотреть файл

@ -60,6 +60,7 @@
B4EF53C3215AF1AB00573E8F /* MSPersona.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4EF53C2215AF1AB00573E8F /* MSPersona.swift */; };
FD053A352224CA33009B6378 /* MSDatePickerControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD053A342224CA33009B6378 /* MSDatePickerControllerTests.swift */; };
FD0D29D62151A3D700E8655E /* MSCardPresenterNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0D29D52151A3D700E8655E /* MSCardPresenterNavigationController.swift */; };
FD1FAE1B2272464B00A5DBA4 /* DateTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1FAE1A2272464B00A5DBA4 /* DateTimePicker.swift */; };
FD256C5B2183B90B00EC9588 /* MSDatePickerSelectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD256C5A2183B90B00EC9588 /* MSDatePickerSelectionManager.swift */; };
FD36F1A9216C0A6900CECBC6 /* MSCardPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA1AF9221487225001AE720 /* MSCardPresentationController.swift */; };
FD4F2A1B2148937100C437D6 /* MSPageCardPresenterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD4F2A1A2148937100C437D6 /* MSPageCardPresenterController.swift */; };
@ -176,6 +177,7 @@
B4EF53C2215AF1AB00573E8F /* MSPersona.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSPersona.swift; sourceTree = "<group>"; };
FD053A342224CA33009B6378 /* MSDatePickerControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSDatePickerControllerTests.swift; sourceTree = "<group>"; };
FD0D29D52151A3D700E8655E /* MSCardPresenterNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSCardPresenterNavigationController.swift; sourceTree = "<group>"; };
FD1FAE1A2272464B00A5DBA4 /* DateTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimePicker.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>"; };
FD4F2A1F214AE20400C437D6 /* MSDatePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSDatePickerController.swift; sourceTree = "<group>"; };
@ -542,6 +544,7 @@
isa = PBXGroup;
children = (
FD77752F21A490BA00033D58 /* MSDateTimePicker.swift */,
FD1FAE1A2272464B00A5DBA4 /* DateTimePicker.swift */,
FD4F2A1C214ADBBF00C437D6 /* Date Picker */,
FD5C8C2A2190C3470063562C /* Date Time Picker */,
);
@ -833,6 +836,7 @@
B46D3F932151D95F0029772C /* MSPersonaCell.swift in Sources */,
B444D6B12181403C0002B4D4 /* UITableViewCell+Extension.swift in Sources */,
FD777529219E3F6C00033D58 /* MSDayOfMonth.swift in Sources */,
FD1FAE1B2272464B00A5DBA4 /* DateTimePicker.swift in Sources */,
A54D97DA217A5FC10072681A /* CALayer+Extensions.swift in Sources */,
FD777527219E305F00033D58 /* Locale+Extensions.swift in Sources */,
B483323721DEB5A00022B4CC /* MSTouchForwardingView.swift in Sources */,

Просмотреть файл

@ -38,5 +38,9 @@ public class OfficeUIFabricFramework: NSObject {
titleAttributes = barButtonItem.titleTextAttributes(for: .normal) ?? [:]
titleAttributes[.font] = MSFonts.body
barButtonItem.setTitleTextAttributes(titleAttributes, for: .normal)
// UISwitch
let `switch` = UISwitch.appearance()
`switch`.onTintColor = MSColors.primary
}
}

Просмотреть файл

@ -227,7 +227,7 @@ class MSDatePickerController: UIViewController, DateTimePicker {
}
@objc private func handleDidTapDone() {
delegate?.dateTimePicker(self, didPickStartDate: startDate, endDate: endDate)
dismiss()
}
}

Просмотреть файл

@ -161,11 +161,6 @@ class MSDateTimePickerController: UIViewController, DateTimePicker {
}
}
private func dismiss() {
delegate?.dateTimePicker(self, didPickStartDate: startDate, endDate: endDate)
presentingViewController?.dismiss(animated: true)
}
@objc private func handleDidSelectDate(_ datePicker: MSDateTimePickerView) {
switch mode {
case .single:

Просмотреть файл

@ -0,0 +1,31 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
import Foundation
// MARK: DateTimePicker
protocol DateTimePicker: class {
var startDate: Date { get set }
var endDate: Date { get set }
var delegate: DateTimePickerDelegate? { get set }
}
extension DateTimePicker where Self: UIViewController {
func dismiss() {
if delegate?.dateTimePicker(self, shouldEndPickingStartDate: startDate, endDate: endDate) ?? true {
delegate?.dateTimePicker(self, didPickStartDate: startDate, endDate: endDate)
presentingViewController?.dismiss(animated: true)
}
}
}
// MARK: - DateTimePickerDelegate
protocol DateTimePickerDelegate: class {
func dateTimePicker(_ dateTimePicker: DateTimePicker, didPickStartDate startDate: Date, endDate: Date)
func dateTimePicker(_ dateTimePicker: DateTimePicker, didSelectStartDate startDate: Date, endDate: Date)
func dateTimePicker(_ dateTimePicker: DateTimePicker, shouldEndPickingStartDate startDate: Date, endDate: Date) -> Bool
}

Просмотреть файл

@ -22,21 +22,8 @@ import Foundation
@objc public protocol MSDateTimePickerDelegate: class {
/// Allows a class to be notified when a user confirms their selected date
func dateTimePicker(_ dateTimePicker: MSDateTimePicker, didPickStartDate startDate: Date, endDate: Date)
}
// MARK: - DateTimePicker
protocol DateTimePicker: class {
var startDate: Date { get set }
var endDate: Date { get set }
var delegate: DateTimePickerDelegate? { get set }
}
// MARK: - DateTimePickerDelegate
protocol DateTimePickerDelegate: class {
func dateTimePicker(_ dateTimePicker: DateTimePicker, didPickStartDate startDate: Date, endDate: Date)
func dateTimePicker(_ dateTimePicker: DateTimePicker, didSelectStartDate startDate: Date, endDate: Date)
/// Allows for some validation and cancellation of picking behavior, including the dismissal of DateTimePicker classes when Done is pressed. If false is returned, the dismissal and `didPickStartDate` delegate calls will not occur. This is not called when dismissing the modal without selection, such as when tapping outside to dismiss.
@objc optional func dateTimePicker(_ dateTimePicker: MSDateTimePicker, shouldEndPickingStartDate startDate: Date, endDate: Date) -> Bool
}
// MARK: - MSDateTimePicker
@ -156,4 +143,8 @@ extension MSDateTimePicker: DateTimePickerDelegate {
picker.endDate = endDate
}
}
func dateTimePicker(_ dateTimePicker: DateTimePicker, shouldEndPickingStartDate startDate: Date, endDate: Date) -> Bool {
return delegate?.dateTimePicker?(self, shouldEndPickingStartDate: startDate, endDate: endDate) ?? true
}
}