mirror of
https://codeberg.org/comaps/comaps
synced 2026-01-07 21:13:55 +00:00
Organic Maps sources as of 02.04.2025 (fad26bbf22ac3da75e01e62aa01e5c8e11861005)
To expand with full Organic Maps and Maps.ME commits history run: git remote add om-historic [om-historic.git repo url] git fetch --tags om-historic git replace squashed-history historic-commits
This commit is contained in:
59
iphone/DatePicker/DatePicker/CalendarHeader.swift
Normal file
59
iphone/DatePicker/DatePicker/CalendarHeader.swift
Normal file
@@ -0,0 +1,59 @@
|
||||
import UIKit
|
||||
|
||||
final class CalendarHeader: UICollectionReusableView {
|
||||
let monthLabel = UILabel()
|
||||
let vStack = UIStackView()
|
||||
let weekdaysStack = UIStackView()
|
||||
var weekdayLabels: [UILabel] = []
|
||||
var theme = DatePickerViewTheme() {
|
||||
didSet {
|
||||
updateTheme()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateTheme() {
|
||||
backgroundColor = theme.monthHeaderBackgroundColor
|
||||
monthLabel.textColor = theme.monthHeaderColor
|
||||
monthLabel.font = theme.monthHeaderFont
|
||||
weekdayLabels.forEach {
|
||||
$0.textColor = theme.weekdaySymbolsColor
|
||||
$0.font = theme.weekdaySymbolsFont
|
||||
}
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
addSubview(vStack)
|
||||
vStack.alignToSuperview()
|
||||
vStack.addArrangedSubview(monthLabel)
|
||||
vStack.addArrangedSubview(weekdaysStack)
|
||||
for _ in 0..<7 {
|
||||
let label = UILabel()
|
||||
label.textAlignment = .center
|
||||
weekdayLabels.append(label)
|
||||
weekdaysStack.addArrangedSubview(label)
|
||||
}
|
||||
vStack.axis = .vertical
|
||||
vStack.distribution = .fillEqually
|
||||
weekdaysStack.axis = .horizontal
|
||||
weekdaysStack.distribution = .fillEqually
|
||||
monthLabel.textAlignment = .center
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
monthLabel.text = nil
|
||||
}
|
||||
|
||||
func config(_ month: String, weekdays: [String], firstWeekday: Int) {
|
||||
monthLabel.text = month
|
||||
for i in 0..<7 {
|
||||
let index = (i + firstWeekday - 1) % 7
|
||||
let label = weekdayLabels[i]
|
||||
label.text = weekdays[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
22
iphone/DatePicker/DatePicker/CalendarLayout.swift
Normal file
22
iphone/DatePicker/DatePicker/CalendarLayout.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
import UIKit
|
||||
|
||||
final class CalendarLayout: UICollectionViewFlowLayout {
|
||||
override func prepare() {
|
||||
super.prepare()
|
||||
|
||||
guard let collectionView = collectionView else { return }
|
||||
let availableWidth = collectionView.bounds.width
|
||||
minimumLineSpacing = 0
|
||||
minimumInteritemSpacing = 0
|
||||
let itemWidth = floor(availableWidth / 7)
|
||||
let spaceLeft = availableWidth - itemWidth * 7
|
||||
sectionInset = UIEdgeInsets(top: 0, left: spaceLeft / 2, bottom: 0, right: spaceLeft / 2)
|
||||
itemSize = CGSize(width: itemWidth, height: itemWidth)
|
||||
headerReferenceSize = CGSize(width: availableWidth, height: 80)
|
||||
sectionHeadersPinToVisibleBounds = true
|
||||
}
|
||||
|
||||
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
36
iphone/DatePicker/DatePicker/Cells/Cell.swift
Normal file
36
iphone/DatePicker/DatePicker/Cells/Cell.swift
Normal file
@@ -0,0 +1,36 @@
|
||||
import UIKit
|
||||
|
||||
class Cell: UICollectionViewCell {
|
||||
let label = UILabel()
|
||||
|
||||
var theme = DatePickerViewTheme() {
|
||||
didSet {
|
||||
updateTheme()
|
||||
}
|
||||
}
|
||||
|
||||
func updateTheme() {
|
||||
label.textColor = theme.dayColor
|
||||
label.font = theme.dayFont
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
addSubviews()
|
||||
label.textAlignment = .center
|
||||
updateTheme()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func addSubviews() {
|
||||
contentView.addSubview(label)
|
||||
label.alignToSuperview()
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
label.text = nil
|
||||
}
|
||||
}
|
||||
85
iphone/DatePicker/DatePicker/Cells/CellStrategy.swift
Normal file
85
iphone/DatePicker/DatePicker/Cells/CellStrategy.swift
Normal file
@@ -0,0 +1,85 @@
|
||||
import UIKit
|
||||
|
||||
enum PositionInRow {
|
||||
case outside
|
||||
case first
|
||||
case middle
|
||||
case last
|
||||
case single
|
||||
}
|
||||
|
||||
enum PositionInRange {
|
||||
case inactive
|
||||
case outside
|
||||
case first
|
||||
case middle
|
||||
case last
|
||||
case single
|
||||
}
|
||||
|
||||
final class CellStrategy {
|
||||
enum CellId: String {
|
||||
case empty = "empty"
|
||||
case regular = "regular"
|
||||
case inactive = "inactive"
|
||||
case selectedSingle = "selectedSingle"
|
||||
case selectedFirst = "selectedFirst"
|
||||
case selectedLast = "selectedLast"
|
||||
case rangeSingle = "rangeSingle"
|
||||
case rangeFirst = "rangeFirst"
|
||||
case rangeMiddle = "rangeMiddle"
|
||||
case rangeLast = "rangeLast"
|
||||
}
|
||||
|
||||
weak var collectionView: UICollectionView?
|
||||
|
||||
init(_ collectionView: UICollectionView) {
|
||||
self.collectionView = collectionView
|
||||
register(EmptyCell.self, cellId: .empty)
|
||||
register(Cell.self, cellId: .regular)
|
||||
register(InactiveCell.self, cellId: .inactive)
|
||||
register(SelectedSingleCell.self, cellId: .selectedSingle)
|
||||
register(SelectedFirstCell.self, cellId: .selectedFirst)
|
||||
register(SelectedLastCell.self, cellId: .selectedLast)
|
||||
register(RangeSingleCell.self, cellId: .rangeSingle)
|
||||
register(RangeFirstCell.self, cellId: .rangeFirst)
|
||||
register(RangeMiddleCell.self, cellId: .rangeMiddle)
|
||||
register(RangeLastCell.self, cellId: .rangeLast)
|
||||
}
|
||||
|
||||
func cell(positionInRange: PositionInRange, positionInRow: PositionInRow, indexPath: IndexPath) -> Cell {
|
||||
let cellId: CellId
|
||||
switch (positionInRange, positionInRow) {
|
||||
case (_, .outside):
|
||||
cellId = .empty
|
||||
case (.inactive, _):
|
||||
cellId = .inactive
|
||||
case (.middle, .first):
|
||||
cellId = .rangeFirst
|
||||
case (.middle, .last):
|
||||
cellId = .rangeLast
|
||||
case (.middle, .single):
|
||||
cellId = .rangeSingle
|
||||
case (.single, _), (.first, .last), (.last, .first), (.first, .single), (.last, .single):
|
||||
cellId = .selectedSingle
|
||||
case (.outside, _):
|
||||
cellId = .regular
|
||||
case (.middle, _):
|
||||
cellId = .rangeMiddle
|
||||
case (.first, _):
|
||||
cellId = .selectedFirst
|
||||
case (.last, _):
|
||||
cellId = .selectedLast
|
||||
}
|
||||
|
||||
return dequeueCell(cellId: cellId, indexPath: indexPath)
|
||||
}
|
||||
|
||||
private func register(_ cellClass: AnyClass, cellId: CellId) {
|
||||
collectionView?.register(cellClass, forCellWithReuseIdentifier: cellId.rawValue)
|
||||
}
|
||||
|
||||
private func dequeueCell(cellId: CellId, indexPath: IndexPath) -> Cell {
|
||||
collectionView!.dequeueReusableCell(withReuseIdentifier: cellId.rawValue, for: indexPath) as! Cell
|
||||
}
|
||||
}
|
||||
5
iphone/DatePicker/DatePicker/Cells/EmptyCell.swift
Normal file
5
iphone/DatePicker/DatePicker/Cells/EmptyCell.swift
Normal file
@@ -0,0 +1,5 @@
|
||||
import UIKit
|
||||
|
||||
final class EmptyCell: Cell {
|
||||
|
||||
}
|
||||
8
iphone/DatePicker/DatePicker/Cells/InactiveCell.swift
Normal file
8
iphone/DatePicker/DatePicker/Cells/InactiveCell.swift
Normal file
@@ -0,0 +1,8 @@
|
||||
import UIKit
|
||||
|
||||
final class InactiveCell: Cell {
|
||||
override func updateTheme() {
|
||||
label.textColor = theme.inactiveDayColor
|
||||
label.font = theme.inactiveDayFont
|
||||
}
|
||||
}
|
||||
23
iphone/DatePicker/DatePicker/Cells/RangeFirstCell.swift
Normal file
23
iphone/DatePicker/DatePicker/Cells/RangeFirstCell.swift
Normal file
@@ -0,0 +1,23 @@
|
||||
import UIKit
|
||||
|
||||
final class RangeFirstCell: Cell {
|
||||
let rangeBgView = UIView()
|
||||
|
||||
override func addSubviews() {
|
||||
contentView.addSubview(rangeBgView)
|
||||
rangeBgView.alignToSuperview(UIEdgeInsets(top: 4, left: 4, bottom: -4, right: 0))
|
||||
rangeBgView.layer.cornerRadius = 8
|
||||
if #available(iOS 13.0, *) {
|
||||
rangeBgView.layer.cornerCurve = .continuous
|
||||
}
|
||||
rangeBgView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
|
||||
super.addSubviews()
|
||||
}
|
||||
|
||||
override func updateTheme() {
|
||||
super.updateTheme()
|
||||
label.textColor = theme.dayColor
|
||||
label.font = theme.dayFont
|
||||
rangeBgView.backgroundColor = theme.selectedRangeBackgroundColor
|
||||
}
|
||||
}
|
||||
23
iphone/DatePicker/DatePicker/Cells/RangeLastCell.swift
Normal file
23
iphone/DatePicker/DatePicker/Cells/RangeLastCell.swift
Normal file
@@ -0,0 +1,23 @@
|
||||
import UIKit
|
||||
|
||||
final class RangeLastCell: Cell {
|
||||
let rangeBgView = UIView()
|
||||
|
||||
override func addSubviews() {
|
||||
contentView.addSubview(rangeBgView)
|
||||
rangeBgView.alignToSuperview(UIEdgeInsets(top: 4, left: 0, bottom: -4, right: -4))
|
||||
rangeBgView.layer.cornerRadius = 8
|
||||
if #available(iOS 13.0, *) {
|
||||
rangeBgView.layer.cornerCurve = .continuous
|
||||
}
|
||||
rangeBgView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
|
||||
super.addSubviews()
|
||||
}
|
||||
|
||||
override func updateTheme() {
|
||||
super.updateTheme()
|
||||
label.textColor = theme.dayColor
|
||||
label.font = theme.dayFont
|
||||
rangeBgView.backgroundColor = theme.selectedRangeBackgroundColor
|
||||
}
|
||||
}
|
||||
18
iphone/DatePicker/DatePicker/Cells/RangeMiddleCell.swift
Normal file
18
iphone/DatePicker/DatePicker/Cells/RangeMiddleCell.swift
Normal file
@@ -0,0 +1,18 @@
|
||||
import UIKit
|
||||
|
||||
final class RangeMiddleCell: Cell {
|
||||
let rangeBgView = UIView()
|
||||
|
||||
override func addSubviews() {
|
||||
contentView.addSubview(rangeBgView)
|
||||
rangeBgView.alignToSuperview(UIEdgeInsets(top: 4, left: 0, bottom: -4, right: 0))
|
||||
super.addSubviews()
|
||||
}
|
||||
|
||||
override func updateTheme() {
|
||||
super.updateTheme()
|
||||
label.textColor = theme.dayColor
|
||||
label.font = theme.dayFont
|
||||
rangeBgView.backgroundColor = theme.selectedRangeBackgroundColor
|
||||
}
|
||||
}
|
||||
22
iphone/DatePicker/DatePicker/Cells/RangeSingleCell.swift
Normal file
22
iphone/DatePicker/DatePicker/Cells/RangeSingleCell.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
import UIKit
|
||||
|
||||
final class RangeSingleCell: Cell {
|
||||
let rangeBgView = UIView()
|
||||
|
||||
override func addSubviews() {
|
||||
contentView.addSubview(rangeBgView)
|
||||
rangeBgView.alignToSuperview(UIEdgeInsets(top: 4, left: 4, bottom: -4, right: -4))
|
||||
rangeBgView.layer.cornerRadius = 8
|
||||
if #available(iOS 13.0, *) {
|
||||
rangeBgView.layer.cornerCurve = .continuous
|
||||
}
|
||||
super.addSubviews()
|
||||
}
|
||||
|
||||
override func updateTheme() {
|
||||
super.updateTheme()
|
||||
label.textColor = theme.dayColor
|
||||
label.font = theme.dayFont
|
||||
rangeBgView.backgroundColor = theme.selectedRangeBackgroundColor
|
||||
}
|
||||
}
|
||||
22
iphone/DatePicker/DatePicker/Cells/SelectedFirstCell.swift
Normal file
22
iphone/DatePicker/DatePicker/Cells/SelectedFirstCell.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
import UIKit
|
||||
|
||||
final class SelectedFirstCell: SelectedSingleCell {
|
||||
let rangeTrailingView = UIView()
|
||||
|
||||
override func addSubviews() {
|
||||
contentView.addSubview(rangeTrailingView)
|
||||
rangeTrailingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
rangeTrailingView.leadingAnchor.constraint(equalTo: contentView.centerXAnchor),
|
||||
rangeTrailingView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4),
|
||||
rangeTrailingView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -4),
|
||||
rangeTrailingView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
|
||||
])
|
||||
super.addSubviews()
|
||||
}
|
||||
|
||||
override func updateTheme() {
|
||||
super.updateTheme()
|
||||
rangeTrailingView.backgroundColor = theme.selectedRangeBackgroundColor
|
||||
}
|
||||
}
|
||||
22
iphone/DatePicker/DatePicker/Cells/SelectedLastCell.swift
Normal file
22
iphone/DatePicker/DatePicker/Cells/SelectedLastCell.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
import UIKit
|
||||
|
||||
final class SelectedLastCell: SelectedSingleCell {
|
||||
let rangeLeadingView = UIView()
|
||||
|
||||
override func addSubviews() {
|
||||
contentView.addSubview(rangeLeadingView)
|
||||
rangeLeadingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
rangeLeadingView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
||||
rangeLeadingView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4),
|
||||
rangeLeadingView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -4),
|
||||
rangeLeadingView.trailingAnchor.constraint(equalTo: contentView.centerXAnchor)
|
||||
])
|
||||
super.addSubviews()
|
||||
}
|
||||
|
||||
override func updateTheme() {
|
||||
super.updateTheme()
|
||||
rangeLeadingView.backgroundColor = theme.selectedRangeBackgroundColor
|
||||
}
|
||||
}
|
||||
22
iphone/DatePicker/DatePicker/Cells/SelectedSingleCell.swift
Normal file
22
iphone/DatePicker/DatePicker/Cells/SelectedSingleCell.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
import UIKit
|
||||
|
||||
class SelectedSingleCell: Cell {
|
||||
let selectedBgView = UIView()
|
||||
|
||||
override func addSubviews() {
|
||||
contentView.addSubview(selectedBgView)
|
||||
selectedBgView.alignToSuperview(UIEdgeInsets(top: 4, left: 4, bottom: -4, right: -4))
|
||||
selectedBgView.layer.cornerRadius = 8
|
||||
if #available(iOS 13.0, *) {
|
||||
selectedBgView.layer.cornerCurve = .continuous
|
||||
}
|
||||
super.addSubviews()
|
||||
}
|
||||
|
||||
override func updateTheme() {
|
||||
super.updateTheme()
|
||||
selectedBgView.backgroundColor = theme.selectedDayBackgroundColor
|
||||
label.textColor = theme.selectedDayColor
|
||||
label.font = theme.selectedDayFont
|
||||
}
|
||||
}
|
||||
225
iphone/DatePicker/DatePicker/DatePickerView.swift
Normal file
225
iphone/DatePicker/DatePicker/DatePickerView.swift
Normal file
@@ -0,0 +1,225 @@
|
||||
import UIKit
|
||||
|
||||
public protocol DatePickerViewDelegate: AnyObject {
|
||||
func datePickerView(_ view: DatePickerView, didSelect date: Date)
|
||||
}
|
||||
|
||||
public final class DatePickerView: UIView {
|
||||
public weak var delegate: DatePickerViewDelegate?
|
||||
public var theme = DatePickerViewTheme() {
|
||||
didSet {
|
||||
collectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
var calendar = Calendar.autoupdatingCurrent
|
||||
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: CalendarLayout())
|
||||
let cellStrategy: CellStrategy
|
||||
var year: Int
|
||||
var firstMonth = 1
|
||||
var numberOfMonths = 13
|
||||
|
||||
public var minimumDate: Date {
|
||||
didSet {
|
||||
year = calendar.component(.year, from: minimumDate)
|
||||
firstMonth = calendar.component(.month, from: minimumDate)
|
||||
numberOfMonths = calendar.dateComponents([.month], from: minimumDate, to: maximumDate).month! + 1
|
||||
collectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
public var maximumDate: Date {
|
||||
didSet {
|
||||
numberOfMonths = calendar.dateComponents([.month], from: minimumDate, to: maximumDate).month! + 1
|
||||
collectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
public var startDate: Date? {
|
||||
didSet {
|
||||
endDate = nil
|
||||
}
|
||||
}
|
||||
|
||||
public var endDate: Date? {
|
||||
didSet {
|
||||
if let endDate = endDate {
|
||||
guard let startDate = startDate, startDate < endDate else { fatalError("startDate must be less then endDate") }
|
||||
}
|
||||
collectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
minimumDate = Date()
|
||||
maximumDate = calendar.date(byAdding: .month, value: numberOfMonths - 1, to: minimumDate)!
|
||||
year = calendar.component(.year, from: minimumDate)
|
||||
firstMonth = calendar.component(.month, from: minimumDate)
|
||||
cellStrategy = CellStrategy(collectionView)
|
||||
super.init(frame: frame)
|
||||
config()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
minimumDate = Date()
|
||||
maximumDate = calendar.date(byAdding: .month, value: numberOfMonths - 1, to: minimumDate)!
|
||||
year = calendar.component(.year, from: minimumDate)
|
||||
firstMonth = calendar.component(.month, from: minimumDate)
|
||||
cellStrategy = CellStrategy(collectionView)
|
||||
super.init(coder: coder)
|
||||
config()
|
||||
}
|
||||
|
||||
private func config() {
|
||||
collectionView.register(CalendarHeader.self,
|
||||
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
|
||||
withReuseIdentifier: "CalendarHeader")
|
||||
collectionView.backgroundColor = .clear
|
||||
addSubview(collectionView)
|
||||
collectionView.alignToSuperview()
|
||||
collectionView.dataSource = self
|
||||
collectionView.delegate = self
|
||||
collectionView.showsVerticalScrollIndicator = false
|
||||
collectionView.showsHorizontalScrollIndicator = false
|
||||
}
|
||||
|
||||
private func dateAtIndexPath(_ indexPath: IndexPath) -> Date? {
|
||||
var components = DateComponents()
|
||||
components.year = year
|
||||
components.month = indexPath.section + firstMonth
|
||||
guard let month = calendar.date(from: components) else { return nil }
|
||||
let firstWeek = calendar.component(.weekOfMonth, from: month)
|
||||
components.weekday = indexPath.item % 7 + calendar.firstWeekday
|
||||
components.weekOfMonth = indexPath.item / 7 + firstWeek
|
||||
guard let date = calendar.date(from: components),
|
||||
calendar.isDate(date, equalTo: month, toGranularity: .month) else { return nil }
|
||||
return date
|
||||
}
|
||||
|
||||
private func positionInRow(_ indexPath: IndexPath) -> PositionInRow {
|
||||
guard let date = dateAtIndexPath(indexPath) else { return .outside }
|
||||
var first = false
|
||||
var last = false
|
||||
|
||||
let startOfMonthComponents = calendar.dateComponents([.year, .month], from: date)
|
||||
guard let startOfMonth = calendar.date(from: startOfMonthComponents) else { return .outside }
|
||||
|
||||
if indexPath.item % 7 == 0 || calendar.isDate(date, equalTo: startOfMonth, toGranularity: .day) {
|
||||
first = true
|
||||
}
|
||||
|
||||
var components = DateComponents()
|
||||
components.month = 1
|
||||
components.day = -1
|
||||
guard let endOfMonth = calendar.date(byAdding: components, to: startOfMonth) else {
|
||||
return first ? .first : .middle
|
||||
}
|
||||
|
||||
if indexPath.item % 7 == 6 || calendar.isDate(date, equalTo: endOfMonth, toGranularity: .day){
|
||||
last = true
|
||||
}
|
||||
|
||||
switch (first, last) {
|
||||
case (true, true):
|
||||
return .single
|
||||
case (true, _):
|
||||
return .first
|
||||
case (_, true):
|
||||
return .last
|
||||
default:
|
||||
return .middle
|
||||
}
|
||||
}
|
||||
|
||||
private func isActiveDate(_ date: Date) -> Bool {
|
||||
return calendar.isDate(date, inSameDayAs: minimumDate) ||
|
||||
calendar.isDate(date, inSameDayAs: maximumDate) ||
|
||||
(date >= minimumDate && date <= maximumDate)
|
||||
}
|
||||
|
||||
private func positionInRange(_ indexPath: IndexPath) -> PositionInRange {
|
||||
guard let date = dateAtIndexPath(indexPath) else { return .inactive }
|
||||
if !isActiveDate(date) { return .inactive }
|
||||
|
||||
var state: PositionInRange = .outside
|
||||
guard let startDate = startDate else { return state }
|
||||
|
||||
if calendar.isDate(date, inSameDayAs: startDate) {
|
||||
state = .first
|
||||
}
|
||||
|
||||
guard let endDate = endDate else {
|
||||
return state == .first ? .single : state
|
||||
}
|
||||
|
||||
if calendar.isDate(date, inSameDayAs: endDate) {
|
||||
state = .last
|
||||
} else if date > startDate && date < endDate {
|
||||
state = .middle
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension DatePickerView: UICollectionViewDataSource {
|
||||
public func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||
numberOfMonths
|
||||
}
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
var components = DateComponents()
|
||||
components.year = year
|
||||
components.month = section + firstMonth
|
||||
let date = calendar.date(from: components)!
|
||||
let range = calendar.range(of: .weekOfMonth, in: .month, for: date)!
|
||||
return range.count * 7
|
||||
}
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cell = cellStrategy.cell(positionInRange: positionInRange(indexPath),
|
||||
positionInRow: positionInRow(indexPath),
|
||||
indexPath: indexPath)
|
||||
cell.theme = theme
|
||||
guard let date = dateAtIndexPath(indexPath) else { return cell }
|
||||
let day = calendar.component(.day, from: date)
|
||||
cell.label.text = "\(day)"
|
||||
return cell
|
||||
}
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView,
|
||||
viewForSupplementaryElementOfKind kind: String,
|
||||
at indexPath: IndexPath) -> UICollectionReusableView {
|
||||
switch kind {
|
||||
case UICollectionView.elementKindSectionHeader:
|
||||
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind,
|
||||
withReuseIdentifier: "CalendarHeader",
|
||||
for: indexPath) as! CalendarHeader
|
||||
header.theme = theme
|
||||
var components = DateComponents()
|
||||
components.year = year
|
||||
components.month = indexPath.section + firstMonth
|
||||
let date = calendar.date(from: components)
|
||||
let realComponents = calendar.dateComponents([.month, .year], from: date!)
|
||||
header.config("\(calendar.standaloneMonthSymbols[realComponents.month! - 1].capitalized) \(realComponents.year!)",
|
||||
weekdays: calendar.shortStandaloneWeekdaySymbols.map { $0.capitalized },
|
||||
firstWeekday: calendar.firstWeekday)
|
||||
return header
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DatePickerView: UICollectionViewDelegate {
|
||||
public func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
|
||||
guard let date = dateAtIndexPath(indexPath) else { return false }
|
||||
return isActiveDate(date)
|
||||
}
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
guard let date = dateAtIndexPath(indexPath) else { fatalError() }
|
||||
delegate?.datePickerView(self, didSelect: date)
|
||||
}
|
||||
}
|
||||
26
iphone/DatePicker/DatePicker/DatePickerViewTheme.swift
Normal file
26
iphone/DatePicker/DatePicker/DatePickerViewTheme.swift
Normal file
@@ -0,0 +1,26 @@
|
||||
import UIKit
|
||||
|
||||
public struct DatePickerViewTheme {
|
||||
public var monthHeaderBackgroundColor: UIColor = .white
|
||||
|
||||
public var monthHeaderColor: UIColor = .darkText
|
||||
public var monthHeaderFont: UIFont = UIFont.systemFont(ofSize: 16, weight: .semibold)
|
||||
public var weekdaySymbolsColor: UIColor = .darkText
|
||||
public var weekdaySymbolsFont: UIFont = UIFont.systemFont(ofSize: 14, weight: .regular)
|
||||
|
||||
public var dayColor: UIColor = .darkText
|
||||
public var dayFont: UIFont = UIFont.systemFont(ofSize: 16, weight: .regular)
|
||||
|
||||
public var inactiveDayColor: UIColor = .lightGray
|
||||
public var inactiveDayFont: UIFont = UIFont.systemFont(ofSize: 16, weight: .regular)
|
||||
|
||||
public var selectedDayColor: UIColor = .white
|
||||
public var selectedDayFont: UIFont = UIFont.systemFont(ofSize: 16, weight: .semibold)
|
||||
public var selectedDayBackgroundColor: UIColor = .systemBlue
|
||||
|
||||
public var selectedRangeColor: UIColor = .white
|
||||
public var selectedRangeFont: UIFont = UIFont.systemFont(ofSize: 16, weight: .regular)
|
||||
public var selectedRangeBackgroundColor: UIColor = UIColor.systemBlue.withAlphaComponent(0.3)
|
||||
|
||||
public init() {}
|
||||
}
|
||||
22
iphone/DatePicker/DatePicker/Info.plist
Normal file
22
iphone/DatePicker/DatePicker/Info.plist
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
</dict>
|
||||
</plist>
|
||||
13
iphone/DatePicker/DatePicker/UIView+Align.swift
Normal file
13
iphone/DatePicker/DatePicker/UIView+Align.swift
Normal file
@@ -0,0 +1,13 @@
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
func alignToSuperview(_ insets: UIEdgeInsets = .zero) {
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
topAnchor.constraint(equalTo: superview!.topAnchor, constant: insets.top),
|
||||
leftAnchor.constraint(equalTo: superview!.leftAnchor, constant: insets.left),
|
||||
bottomAnchor.constraint(equalTo: superview!.bottomAnchor, constant: insets.bottom),
|
||||
rightAnchor.constraint(equalTo: superview!.rightAnchor, constant: insets.right)
|
||||
])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user