Various Improvements

This commit is contained in:
Ilya Laktyushin
2021-02-06 22:38:32 +04:00
parent ca993ccfa5
commit 1f6b5ba9e0
79 changed files with 38285 additions and 36596 deletions

View File

@@ -13,8 +13,9 @@ public final class DatePickerTheme: Equatable {
public let disabledColor: UIColor
public let selectionColor: UIColor
public let selectionTextColor: UIColor
public let separatorColor: UIColor
public init(backgroundColor: UIColor, textColor: UIColor, secondaryTextColor: UIColor, accentColor: UIColor, disabledColor: UIColor, selectionColor: UIColor, selectionTextColor: UIColor) {
public init(backgroundColor: UIColor, textColor: UIColor, secondaryTextColor: UIColor, accentColor: UIColor, disabledColor: UIColor, selectionColor: UIColor, selectionTextColor: UIColor, separatorColor: UIColor) {
self.backgroundColor = backgroundColor
self.textColor = textColor
self.secondaryTextColor = secondaryTextColor
@@ -22,6 +23,7 @@ public final class DatePickerTheme: Equatable {
self.disabledColor = disabledColor
self.selectionColor = selectionColor
self.selectionTextColor = selectionTextColor
self.separatorColor = separatorColor
}
public static func ==(lhs: DatePickerTheme, rhs: DatePickerTheme) -> Bool {
@@ -43,13 +45,16 @@ public final class DatePickerTheme: Equatable {
if lhs.selectionTextColor != rhs.selectionTextColor {
return false
}
if lhs.separatorColor != rhs.separatorColor {
return false
}
return true
}
}
public extension DatePickerTheme {
convenience init(theme: PresentationTheme) {
self.init(backgroundColor: theme.list.itemBlocksBackgroundColor, textColor: theme.list.itemPrimaryTextColor, secondaryTextColor: theme.list.itemSecondaryTextColor, accentColor: theme.list.itemAccentColor, disabledColor: theme.list.itemDisabledTextColor, selectionColor: theme.list.itemCheckColors.fillColor, selectionTextColor: theme.list.itemCheckColors.foregroundColor)
self.init(backgroundColor: theme.list.itemBlocksBackgroundColor, textColor: theme.list.itemPrimaryTextColor, secondaryTextColor: theme.list.itemSecondaryTextColor, accentColor: theme.list.itemAccentColor, disabledColor: theme.list.itemDisabledTextColor, selectionColor: theme.list.itemCheckColors.fillColor, selectionTextColor: theme.list.itemCheckColors.foregroundColor, separatorColor: theme.list.itemBlocksSeparatorColor)
}
}
@@ -104,6 +109,12 @@ private func generateNavigationArrowImage(color: UIColor, mirror: Bool) -> UIIma
})
}
private func yearRange(for state: DatePickerNode.State) -> Range<Int> {
let minYear = calendar.component(.year, from: state.minDate)
let maxYear = calendar.component(.year, from: state.maxDate)
return minYear ..< maxYear + 1
}
public final class DatePickerNode: ASDisplayNode {
class MonthNode: ASDisplayNode {
private let month: Date
@@ -117,29 +128,9 @@ public final class DatePickerNode: ASDisplayNode {
}
}
var maximumDate: Date? {
didSet {
if let size = self.validSize {
self.updateLayout(size: size)
}
}
}
var minimumDate: Date? {
didSet {
if let size = self.validSize {
self.updateLayout(size: size)
}
}
}
var date: Date? {
didSet {
if let size = self.validSize {
self.updateLayout(size: size)
}
}
}
var maximumDate: Date?
var minimumDate: Date?
var date: Date?
private var validSize: CGSize?
@@ -267,6 +258,7 @@ public final class DatePickerNode: ASDisplayNode {
private let timeTitleNode: ImmediateTextNode
private let timeFieldNode: ASImageNode
private let timeSeparatorNode: ASDisplayNode
private let dayNodes: [ImmediateTextNode]
private var currentIndex = 0
@@ -290,6 +282,8 @@ public final class DatePickerNode: ASDisplayNode {
private var validLayout: CGSize?
public var valueUpdated: ((Date) -> Void)?
public var maximumDate: Date {
get {
return self.state.maxDate
@@ -348,10 +342,15 @@ public final class DatePickerNode: ASDisplayNode {
self.state = State(minDate: telegramReleaseDate, maxDate: upperLimitDate, date: Date(), displayingMonthSelection: false, selectedMonth: monthForDate(Date()))
self.timeTitleNode = ImmediateTextNode()
self.timeTitleNode.attributedText = NSAttributedString(string: "Time", font: Font.regular(17.0), textColor: theme.textColor)
self.timeFieldNode = ASImageNode()
self.timeFieldNode.displaysAsynchronously = false
self.timeFieldNode.displayWithoutProcessing = true
self.timeSeparatorNode = ASDisplayNode()
self.timeSeparatorNode.backgroundColor = theme.separatorColor
self.dayNodes = (0..<7).map { _ in ImmediateTextNode() }
self.contentNode = ASDisplayNode()
@@ -361,8 +360,9 @@ public final class DatePickerNode: ASDisplayNode {
self.pickerBackgroundNode.backgroundColor = theme.backgroundColor
self.pickerBackgroundNode.isUserInteractionEnabled = false
self.pickerNode = MonthPickerNode(theme: theme, strings: strings, date: self.state.date, yearRange: 2013 ..< 2038, valueChanged: { date in
var monthChangedImpl: ((Date) -> Void)?
self.pickerNode = MonthPickerNode(theme: theme, strings: strings, date: self.state.date, yearRange: yearRange(for: self.state), valueChanged: { date in
monthChangedImpl?(date)
})
self.monthButtonNode = HighlightTrackingButtonNode()
@@ -380,9 +380,13 @@ public final class DatePickerNode: ASDisplayNode {
self.clipsToBounds = true
self.backgroundColor = theme.backgroundColor
self.addSubnode(self.contentNode)
self.addSubnode(self.timeTitleNode)
self.addSubnode(self.timeFieldNode)
self.addSubnode(self.timeSeparatorNode)
self.addSubnode(self.contentNode)
self.dayNodes.forEach { self.addSubnode($0) }
self.addSubnode(self.previousButtonNode)
@@ -420,6 +424,15 @@ public final class DatePickerNode: ASDisplayNode {
self.previousButtonNode.addTarget(self, action: #selector(self.previousButtonPressed), forControlEvents: .touchUpInside)
self.nextButtonNode.addTarget(self, action: #selector(self.nextButtonPressed), forControlEvents: .touchUpInside)
monthChangedImpl = { [weak self] date in
if let strongSelf = self {
let updatedState = State(minDate: strongSelf.state.minDate, maxDate: strongSelf.state.maxDate, date: date, displayingMonthSelection: strongSelf.state.displayingMonthSelection, selectedMonth: monthForDate(date))
strongSelf.updateState(updatedState, animated: false)
strongSelf.valueUpdated?(date)
}
}
}
override public func didLoad() {
@@ -436,6 +449,7 @@ public final class DatePickerNode: ASDisplayNode {
self.state = state
if previousState.minDate != state.minDate || previousState.maxDate != state.maxDate {
self.pickerNode.yearRange = yearRange(for: state)
self.setupItems()
} else if previousState.selectedMonth != state.selectedMonth {
for i in 0 ..< self.months.count {
@@ -445,6 +459,7 @@ public final class DatePickerNode: ASDisplayNode {
}
}
}
self.pickerNode.date = self.state.date
if let size = self.validLayout {
self.updateLayout(size: size, transition: animated ? .animated(duration: 0.3, curve: .spring) : .immediate)
@@ -455,9 +470,7 @@ public final class DatePickerNode: ASDisplayNode {
let startMonth = monthForDate(self.state.minDate)
let endMonth = monthForDate(self.state.maxDate)
let selectedMonth = monthForDate(self.state.selectedMonth)
let calendar = Calendar.current
var currentIndex = 0
var months: [Date] = [startMonth]
@@ -524,7 +537,7 @@ public final class DatePickerNode: ASDisplayNode {
}
self.transitionFraction = transitionFraction
if let size = self.validLayout {
let topInset: CGFloat = 78.0
let topInset: CGFloat = 78.0 + 44.0
let containerSize = CGSize(width: size.width, height: size.height - topInset)
self.updateItems(size: containerSize, transition: .animated(duration: 0.3, curve: .spring))
}
@@ -565,10 +578,13 @@ public final class DatePickerNode: ASDisplayNode {
var wasAdded = false
if let current = self.monthNodes[self.months[i]] {
itemNode = current
current.minimumDate = self.state.minDate
current.maximumDate = self.state.maxDate
current.date = self.state.date
current.updateLayout(size: size)
} else {
wasAdded = true
let addedItemNode = MonthNode(theme: self.theme, month: self.months[i], minimumDate: self.minimumDate, maximumDate: self.maximumDate, date: self.date)
let addedItemNode = MonthNode(theme: self.theme, month: self.months[i], minimumDate: self.state.minDate, maximumDate: self.state.maxDate, date: self.state.date)
itemNode = addedItemNode
self.monthNodes[self.months[i]] = addedItemNode
self.contentNode.addSubnode(addedItemNode)
@@ -604,18 +620,29 @@ public final class DatePickerNode: ASDisplayNode {
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = size
let topInset: CGFloat = 78.0
let timeHeight: CGFloat = 44.0
let topInset: CGFloat = 78.0 + timeHeight
let sideInset: CGFloat = 16.0
let month = monthForDate(self.state.selectedMonth)
let components = calendar.dateComponents([.month, .year], from: month)
let timeTitleSize = self.timeTitleNode.updateLayout(size)
self.timeTitleNode.frame = CGRect(origin: CGPoint(x: 16.0, y: 11.0), size: timeTitleSize)
self.timeSeparatorNode.frame = CGRect(x: 16.0, y: timeHeight, width: size.width - 16.0, height: UIScreenPixel)
self.monthTextNode.attributedText = NSAttributedString(string: stringForMonth(strings: self.strings, month: components.month.flatMap { Int32($0) - 1 } ?? 0, ofYear: components.year.flatMap { Int32($0) - 1900 } ?? 100), font: controlFont, textColor: theme.textColor)
let monthSize = self.monthTextNode.updateLayout(size)
let monthTextFrame = CGRect(x: sideInset, y: 10.0, width: monthSize.width, height: monthSize.height)
let monthTextFrame = CGRect(x: sideInset, y: 11.0 + timeHeight, width: monthSize.width, height: monthSize.height)
self.monthTextNode.frame = monthTextFrame
self.monthArrowNode.frame = CGRect(x: monthTextFrame.maxX + 10.0, y: monthTextFrame.minY + 4.0, width: 7.0, height: 12.0)
let monthArrowFrame = CGRect(x: monthTextFrame.maxX + 10.0, y: monthTextFrame.minY + 4.0, width: 7.0, height: 12.0)
self.monthArrowNode.position = monthArrowFrame.center
self.monthArrowNode.bounds = CGRect(origin: CGPoint(), size: monthArrowFrame.size)
transition.updateTransformRotation(node: self.monthArrowNode, angle: self.state.displayingMonthSelection ? CGFloat.pi / 2.0 : 0.0)
self.monthButtonNode.frame = monthTextFrame.inset(by: UIEdgeInsets(top: -6.0, left: -6.0, bottom: -6.0, right: -30.0))
self.previousButtonNode.frame = CGRect(x: size.width - sideInset - 54.0, y: monthTextFrame.minY + 1.0, width: 10.0, height: 17.0)
@@ -629,7 +656,7 @@ public final class DatePickerNode: ASDisplayNode {
dayNode.attributedText = NSAttributedString(string: shortStringForDayOfWeek(strings: self.strings, day: Int32(i)).uppercased(), font: dayFont, textColor: theme.secondaryTextColor)
let textSize = dayNode.updateLayout(size)
let cellFrame = CGRect(x: daysSideInset + CGFloat(i) * cellSize, y: 40.0, width: cellSize, height: cellSize)
let cellFrame = CGRect(x: daysSideInset + CGFloat(i) * cellSize, y: topInset - 38.0, width: cellSize, height: cellSize)
let textFrame = CGRect(origin: CGPoint(x: cellFrame.minX + floor((cellFrame.width - textSize.width) / 2.0), y: cellFrame.minY + floor((cellFrame.height - textSize.height) / 2.0)), size: textSize)
dayNode.frame = textFrame
@@ -679,10 +706,10 @@ private final class MonthPickerNode: ASDisplayNode, UIPickerViewDelegate, UIPick
private let theme: DatePickerTheme
private let strings: PresentationStrings
private var date: Date
private var yearRange: Range<Int> {
var date: Date
var yearRange: Range<Int> {
didSet {
self.pickerView.reloadAllComponents()
self.reload()
}
}
@@ -706,9 +733,16 @@ private final class MonthPickerNode: ASDisplayNode, UIPickerViewDelegate, UIPick
self.pickerView.dataSource = self
self.view.addSubview(self.pickerView)
self.reload()
}
private func reload() {
self.pickerView.reloadAllComponents()
// self.pickerView.selectRow(index, inComponent: 0, animated: false)
let month = calendar.component(.month, from: date)
let year = calendar.component(.year, from: date)
self.pickerView.selectRow(month - 1, inComponent: 0, animated: false)
self.pickerView.selectRow(year - yearRange.startIndex, inComponent: 1, animated: false)
}
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
@@ -742,7 +776,23 @@ private final class MonthPickerNode: ASDisplayNode, UIPickerViewDelegate, UIPick
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// self.valueChanged(timeoutValues[row])
let month = pickerView.selectedRow(inComponent: 0) + 1
let year = self.yearRange.startIndex + pickerView.selectedRow(inComponent: 1)
var components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: self.date)
let day = components.day ?? 1
components.day = 1
components.month = month
components.year = year
let tempDate = calendar.date(from: components)!
let numberOfDays = calendar.range(of: .day, in: .month, for: tempDate)!.count
components.day = min(day, numberOfDays)
let date = calendar.date(from: components)!
self.date = date
self.valueChanged(date)
}
override func layout() {