Time Picker Improvements

This commit is contained in:
Ilya Laktyushin 2021-02-18 06:53:42 +04:00
parent fa645bba66
commit 532dda0388
2 changed files with 111 additions and 56 deletions

View File

@ -322,6 +322,7 @@ public final class DatePickerNode: ASDisplayNode {
self.updateState(updatedState, animated: false)
self.pickerNode.minimumDate = newValue
self.timePickerNode.minimumDate = newValue
if let size = self.validLayout {
let _ = self.updateLayout(size: size, transition: .immediate)
@ -342,6 +343,7 @@ public final class DatePickerNode: ASDisplayNode {
self.updateState(updatedState, animated: false)
self.pickerNode.maximumDate = newValue
self.timePickerNode.maximumDate = newValue
if let size = self.validLayout {
let _ = self.updateLayout(size: size, transition: .immediate)
@ -396,6 +398,9 @@ public final class DatePickerNode: ASDisplayNode {
self.pickerNode.minimumDate = self.state.minDate
self.pickerNode.maximumDate = self.state.maxDate
self.timePickerNode.minimumDate = self.state.minDate
self.timePickerNode.maximumDate = self.state.maxDate
self.monthButtonNode = HighlightTrackingButtonNode()
self.monthTextNode = ImmediateTextNode()
self.monthArrowNode = ASImageNode()
@ -921,9 +926,7 @@ private class TimeInputView: UIView, UIKeyInput {
var textUpdated: ((String) -> Void)?
override func becomeFirstResponder() -> Bool {
if self.isFirstResponder {
self.didReset = false
}
self.didReset = false
let result = super.becomeFirstResponder()
self.focusUpdated?(true)
return result
@ -937,7 +940,7 @@ private class TimeInputView: UIView, UIKeyInput {
var length: Int = 4
private var didReset = false
var didReset = false
private let nonDigits = CharacterSet.decimalDigits.inverted
func insertText(_ text: String) {
if text.rangeOfCharacter(from: nonDigits) != nil {
@ -1024,6 +1027,12 @@ private class TimeInputNode: ASDisplayNode {
view.textUpdated = self.textUpdated
}
}
func reset() {
if let view = self.view as? TimeInputView {
view.didReset = false
}
}
}
private final class TimePickerNode: ASDisplayNode {
@ -1064,8 +1073,9 @@ private final class TimePickerNode: ASDisplayNode {
}
}
}
var minDate: Date?
var maxDate: Date?
var minimumDate: Date?
var maximumDate: Date?
var valueChanged: ((Date) -> Void)?
@ -1205,23 +1215,7 @@ private final class TimePickerNode: ASDisplayNode {
}
}
self.hoursNode.selected = { [weak self] index in
guard let strongSelf = self else {
return
}
switch dateTimeFormat.timeFormat {
case .military:
let hour = index
if let date = strongSelf.date {
var components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: date)
components.hour = hour
if let newDate = calendar.date(from: components) {
strongSelf.date = newDate
strongSelf.valueChanged?(newDate)
}
}
case .regular:
break
}
self?.updateTime()
}
self.minutesNode.count = {
@ -1248,29 +1242,59 @@ private final class TimePickerNode: ASDisplayNode {
}
}
}
self.minutesNode.selected = { [weak self] index in
guard let strongSelf = self else {
return
}
switch dateTimeFormat.timeFormat {
case .military:
let minute = index
if let date = strongSelf.date {
var components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: date)
components.minute = minute
if let newDate = calendar.date(from: components) {
strongSelf.date = newDate
strongSelf.valueChanged?(newDate)
}
}
case .regular:
break
}
self.minutesNode.selected = { [weak self] _ in
self?.updateTime()
}
self.update()
}
private func updateTime() {
switch self.dateTimeFormat.timeFormat {
case .military:
let hour = self.hoursNode.currentSelectedIndex
let minute = self.minutesNode.currentSelectedIndex
let date = self.date ?? Date()
var components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: date)
components.hour = hour
components.minute = minute
if var newDate = calendar.date(from: components) {
if let minDate = self.minimumDate, newDate <= minDate {
if let nextDate = calendar.date(byAdding: .day, value: 1, to: newDate) {
newDate = nextDate
}
}
self.date = newDate
self.valueChanged?(newDate)
}
case .regular:
let hour = self.hoursNode.currentSelectedIndex
let minute = self.minutesNode.currentSelectedIndex
let date = self.date ?? Date()
var components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: date)
if self.amPMSelectorNode.selectedIndex == 0 {
components.hour = hour >= 12 ? hour - 12 : hour
} else if self.amPMSelectorNode.selectedIndex == 1 {
components.hour = hour < 12 ? hour + 12 : hour
}
components.minute = minute
if var newDate = calendar.date(from: components) {
if let minDate = self.minimumDate, newDate <= minDate {
if let nextDate = calendar.date(byAdding: .day, value: 1, to: newDate) {
newDate = nextDate
}
}
self.date = newDate
self.valueChanged?(newDate)
}
}
}
override func didLoad() {
super.didLoad()
@ -1355,6 +1379,23 @@ private final class TimePickerNode: ASDisplayNode {
private var selection: Selection {
didSet {
self.typing = false
self.inputNode.reset()
switch self.selection {
case .none:
break
case .hours:
self.inputNode.text = self.hoursNode.titleAt?(self.hoursNode.currentSelectedIndex) ?? ""
self.inputNode.length = 2
case .minutes:
self.inputNode.text = self.minutesNode.titleAt?(self.minutesNode.currentSelectedIndex) ?? ""
self.inputNode.length = 2
case .all:
let hours = self.minutesNode.titleAt?(self.hoursNode.currentSelectedIndex) ?? ""
let minutes = self.minutesNode.titleAt?(self.minutesNode.currentSelectedIndex) ?? ""
self.inputNode.text = "\(hours)\(minutes)"
self.inputNode.length = 4
}
self.update()
}
}
@ -1417,8 +1458,6 @@ private final class TimePickerNode: ASDisplayNode {
self.hoursNode.isHidden = false
self.minutesNode.isHidden = false
}
self.inputNode.length = 2
case .minutes:
colonColor = self.theme.textColor
self.colonNode.alpha = 0.35
@ -1446,8 +1485,6 @@ private final class TimePickerNode: ASDisplayNode {
self.hoursNode.isHidden = false
self.minutesNode.isHidden = false
}
self.inputNode.length = 2
case .all:
colonColor = self.theme.accentColor
self.colonNode.alpha = 1.0
@ -1475,8 +1512,6 @@ private final class TimePickerNode: ASDisplayNode {
self.hoursNode.isHidden = false
self.minutesNode.isHidden = false
}
self.inputNode.length = 4
}
if let size = self.validLayout {

View File

@ -63,7 +63,9 @@ open class TapeNode: ASDisplayNode {
return UITableView()
}()
private var infinityRowsMultiplier: Int = 1
private var infinityRowsMultiplier: Int {
return generateInfinityRowsMultiplier()
}
var currentSelectedRow: Int?
var currentSelectedIndex: Int {
@ -86,8 +88,6 @@ open class TapeNode: ASDisplayNode {
}
fileprivate func setup() {
self.infinityRowsMultiplier = self.generateInfinityRowsMultiplier()
if #available(iOS 11.0, *) {
self.tableView.contentInsetAdjustmentBehavior = .never
}
@ -162,23 +162,41 @@ open class TapeNode: ASDisplayNode {
if self.currentSelectedIndex == self.indexForRow(row) {
return
}
var finalRow = row
var finalRow = row
if row <= self.numberOfRows {
let middleMultiplier = (self.infinityRowsMultiplier / 2)
let middleMultiplier = self.infinityRowsMultiplier / 2
let middleIndex = self.numberOfRows * middleMultiplier
finalRow = middleIndex - (self.numberOfRows - finalRow)
}
self.currentSelectedRow = finalRow
if let currentSelectedRow = self.currentSelectedRow {
self.tableView.setContentOffset(CGPoint(x: 0.0, y: CGFloat(currentSelectedRow) * self.rowHeight), animated: animated)
}
}
func selectMiddleRow() {
var finalRow = self.currentSelectedIndex
let middleMultiplier = self.infinityRowsMultiplier / 2
let middleIndex = self.numberOfRows * middleMultiplier
finalRow = middleIndex - (self.numberOfRows - finalRow)
self.currentSelectedRow = finalRow
if let currentSelectedRow = self.currentSelectedRow {
self.tableView.setContentOffset(CGPoint(x: 0.0, y: CGFloat(currentSelectedRow) * self.rowHeight), animated: false)
}
}
open func reloadPickerView() {
self.tableView.reloadData()
}
private func hapticTap() {
self.hapticFeedback.impact(.light)
AudioServicesPlaySystemSound(1157)
}
}
extension TapeNode: UITableViewDataSource {
@ -248,15 +266,18 @@ extension TapeNode: UIScrollViewDelegate {
if !decelerate {
self.isScrolling = false
self.isScrollingUpdated?(false)
self.selectMiddleRow()
}
}
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
self.isScrolling = false
self.isScrollingUpdated?(false)
self.selectMiddleRow()
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
let partialRow = Float(scrollView.contentOffset.y / self.rowHeight)
let roundedRow = Int(lroundf(partialRow))
@ -264,8 +285,7 @@ extension TapeNode: UIScrollViewDelegate {
if self.previousRoundedRow != roundedRow && self.isScrolling {
self.previousRoundedRow = roundedRow
self.hapticFeedback.impact()
AudioServicesPlaySystemSound(1157)
self.hapticTap()
}
}
}