DatePickerRow.swift 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // DateRow.swift
  2. // Eureka ( https://github.com/xmartlabs/Eureka )
  3. //
  4. // Copyright (c) 2016 Xmartlabs SRL ( http://xmartlabs.com )
  5. //
  6. //
  7. // Permission is hereby granted, free of charge, to any person obtaining a copy
  8. // of this software and associated documentation files (the "Software"), to deal
  9. // in the Software without restriction, including without limitation the rights
  10. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. // copies of the Software, and to permit persons to whom the Software is
  12. // furnished to do so, subject to the following conditions:
  13. //
  14. // The above copyright notice and this permission notice shall be included in
  15. // all copies or substantial portions of the Software.
  16. //
  17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. // THE SOFTWARE.
  24. import Foundation
  25. import UIKit
  26. open class DatePickerCell: Cell<Date>, CellType {
  27. @IBOutlet weak public var datePicker: UIDatePicker!
  28. public required init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
  29. let datePicker = UIDatePicker()
  30. self.datePicker = datePicker
  31. self.datePicker.translatesAutoresizingMaskIntoConstraints = false
  32. super.init(style: style, reuseIdentifier: reuseIdentifier)
  33. self.contentView.addSubview(self.datePicker)
  34. self.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[picker]-0-|", options: [], metrics: nil, views: ["picker": self.datePicker!]))
  35. self.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[picker]-0-|", options: [], metrics: nil, views: ["picker": self.datePicker!]))
  36. }
  37. required public init?(coder aDecoder: NSCoder) {
  38. super.init(coder: aDecoder)
  39. }
  40. open override func setup() {
  41. super.setup()
  42. selectionStyle = .none
  43. accessoryType = .none
  44. editingAccessoryType = .none
  45. height = { UITableView.automaticDimension }
  46. datePicker.datePickerMode = datePickerMode()
  47. datePicker.addTarget(self, action: #selector(DatePickerCell.datePickerValueChanged(_:)), for: .valueChanged)
  48. if datePicker.datePickerMode != .countDownTimer {
  49. #if swift(>=5.2)
  50. if #available(iOS 14.0, *) {
  51. #if swift(>=5.3) && !(os(OSX) || (os(iOS) && targetEnvironment(macCatalyst)))
  52. datePicker.preferredDatePickerStyle = .inline
  53. #else
  54. datePicker.preferredDatePickerStyle = .wheels
  55. #endif
  56. } else if #available(iOS 13.4, *) {
  57. datePicker.preferredDatePickerStyle = .wheels
  58. }
  59. #endif
  60. }
  61. }
  62. deinit {
  63. datePicker?.removeTarget(self, action: nil, for: .allEvents)
  64. }
  65. open override func update() {
  66. super.update()
  67. selectionStyle = row.isDisabled ? .none : .default
  68. datePicker.isUserInteractionEnabled = !row.isDisabled
  69. detailTextLabel?.text = nil
  70. textLabel?.text = nil
  71. datePicker.setDate(row.value ?? Date(), animated: row is CountDownPickerRow)
  72. datePicker.minimumDate = (row as? DatePickerRowProtocol)?.minimumDate
  73. datePicker.maximumDate = (row as? DatePickerRowProtocol)?.maximumDate
  74. if let minuteIntervalValue = (row as? DatePickerRowProtocol)?.minuteInterval {
  75. datePicker.minuteInterval = minuteIntervalValue
  76. }
  77. }
  78. @objc(pickerDateChanged:) func datePickerValueChanged(_ sender: UIDatePicker) {
  79. row?.value = sender.date
  80. // workaround for UIDatePicker bug when it doesn't trigger "value changed" event after trying to pick 00:00 value
  81. // for details see this comment: https://stackoverflow.com/questions/20181980/uidatepicker-bug-uicontroleventvaluechanged-after-hitting-minimum-internal#comment56681891_20204225
  82. if sender.datePickerMode == .countDownTimer && sender.countDownDuration == TimeInterval(sender.minuteInterval * 60) {
  83. datePicker.countDownDuration = sender.countDownDuration
  84. }
  85. }
  86. private func datePickerMode() -> UIDatePicker.Mode {
  87. switch row {
  88. case is DatePickerRow:
  89. return .date
  90. case is TimePickerRow:
  91. return .time
  92. case is DateTimePickerRow:
  93. return .dateAndTime
  94. case is CountDownPickerRow:
  95. return .countDownTimer
  96. default:
  97. return .date
  98. }
  99. }
  100. }
  101. open class _DatePickerRow: Row<DatePickerCell>, DatePickerRowProtocol {
  102. open var minimumDate: Date?
  103. open var maximumDate: Date?
  104. open var minuteInterval: Int?
  105. required public init(tag: String?) {
  106. super.init(tag: tag)
  107. displayValueFor = nil
  108. }
  109. }
  110. /// A row with an Date as value where the user can select a date directly.
  111. public final class DatePickerRow: _DatePickerRow, RowType {
  112. public required init(tag: String?) {
  113. super.init(tag: tag)
  114. }
  115. }
  116. /// A row with an Date as value where the user can select a time directly.
  117. public final class TimePickerRow: _DatePickerRow, RowType {
  118. public required init(tag: String?) {
  119. super.init(tag: tag)
  120. }
  121. }
  122. /// A row with an Date as value where the user can select date and time directly.
  123. public final class DateTimePickerRow: _DatePickerRow, RowType {
  124. public required init(tag: String?) {
  125. super.init(tag: tag)
  126. }
  127. }
  128. /// A row with an Date as value where the user can select hour and minute as a countdown timer.
  129. public final class CountDownPickerRow: _DatePickerRow, RowType {
  130. public required init(tag: String?) {
  131. super.init(tag: tag)
  132. }
  133. }