Skip to content

Commit

Permalink
Support custom calendar (Issue #40)
Browse files Browse the repository at this point in the history
* support custom calendar

 - add button with custom calendar in example
 - readme

* unavailable  locale in CurrentValueView and MonthHeader
  • Loading branch information
UriyDevyataev authored Nov 14, 2023
1 parent 4aee94f commit 0e37d90
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 45 deletions.
49 changes: 45 additions & 4 deletions Example/Source/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,27 @@ class ViewController: UIViewController {
return button
}()

private lazy var chooseSingleButtonWithCustomCalendar: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Choose single date with custom calendar", for: .normal)
button.addTarget(self, action: #selector(self.chooseSingleDateWithCustomCalendar), for: .touchUpInside)
return button
}()

// MARK: - Variables

private lazy var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy"
return formatter
}()

private var currentValue: FastisValue? {
didSet {
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy"
if let rangeValue = self.currentValue as? FastisRange {
self.currentDateLabel.text = formatter.string(from: rangeValue.fromDate) + " - " + formatter.string(from: rangeValue.toDate)
self.currentDateLabel.text = self.dateFormatter.string(from: rangeValue.fromDate) + " - " + self.dateFormatter.string(from: rangeValue.toDate)
} else if let date = self.currentValue as? Date {
self.currentDateLabel.text = formatter.string(from: date)
self.currentDateLabel.text = self.dateFormatter.string(from: date)
} else {
self.currentDateLabel.text = "Choose a date"
}
Expand Down Expand Up @@ -79,6 +90,7 @@ class ViewController: UIViewController {
self.containerView.setCustomSpacing(32, after: self.currentDateLabel)
self.containerView.addArrangedSubview(self.chooseRangeButton)
self.containerView.addArrangedSubview(self.chooseSingleButton)
self.containerView.addArrangedSubview(self.chooseSingleButtonWithCustomCalendar)
self.view.addSubview(self.containerView)
}

Expand All @@ -97,6 +109,7 @@ class ViewController: UIViewController {

@objc
private func chooseRange() {
self.dateFormatter.calendar = .current
let fastisController = FastisController(mode: .range)
fastisController.title = "Choose range"
fastisController.initialValue = self.currentValue as? FastisRange
Expand All @@ -117,6 +130,7 @@ class ViewController: UIViewController {

@objc
private func chooseSingleDate() {
self.dateFormatter.calendar = .current
let fastisController = FastisController(mode: .single)
fastisController.title = "Choose date"
fastisController.initialValue = self.currentValue as? Date
Expand All @@ -133,4 +147,31 @@ class ViewController: UIViewController {
fastisController.present(above: self)
}

@objc
private func chooseSingleDateWithCustomCalendar() {
var customConfig: FastisConfig = .default
var calendar: Calendar = .init(identifier: .islamicUmmAlQura)
calendar.locale = .autoupdatingCurrent
customConfig.calendar = calendar

self.dateFormatter.calendar = calendar

let fastisController = FastisController(mode: .range, config: customConfig)
fastisController.title = "Choose range"
fastisController.initialValue = self.currentValue as? FastisRange
fastisController.minimumDate = calendar.date(byAdding: .month, value: -2, to: Date())
fastisController.maximumDate = calendar.date(byAdding: .month, value: 3, to: Date())
fastisController.allowToChooseNilDate = true
fastisController.shortcuts = [.today, .lastWeek, .lastMonth]
fastisController.dismissHandler = { [weak self] action in
switch action {
case .done(let newValue):
self?.currentValue = newValue
case .cancel:
print("any actions")
}
}
fastisController.present(above: self)
}

}
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,19 @@ var customConfig = FastisConfig.default
customConfig.controller.dayCell.dateLabelColor = .blue
let fastisController = FastisController(mode: .range, config: customConfig)
```

To customise a special FastisController instance with custom calendar:

```swift
var customConfig: FastisConfig = .default
var calendar: Calendar = .init(identifier: .islamicUmmAlQura)
calendar.locale = .autoupdatingCurrent
customConfig.calendar = calendar
let fastisController = FastisController(mode: .range, config: customConfig)
fastisController.minimumDate = calendar.date(byAdding: .month, value: -2, to: Date())
fastisController.maximumDate = calendar.date(byAdding: .month, value: 3, to: Date())
```

To customise a today cell:

```swift
Expand Down
32 changes: 16 additions & 16 deletions Sources/Models/Shortcut.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import Foundation
Also you can create your own shortcut:

```swift
var customShortcut = FastisShortcut(name: "Today") {
var customShortcut = FastisShortcut(name: "Today") { calendar in
let now = Date()
return FastisRange(from: now.startOfDay(), to: now.endOfDay())
}
Expand All @@ -35,13 +35,13 @@ public struct FastisShortcut<Value: FastisValue>: Hashable {
public var name: String

/// Tap handler
public var action: () -> Value
public var action: (Calendar) -> Value

/// Create a shortcut
/// - Parameters:
/// - name: Display name of shortcut
/// - action: Tap handler
public init(name: String, action: @escaping () -> Value) {
public init(name: String, action: @escaping (Calendar) -> Value) {
self.name = name
self.action = action
}
Expand All @@ -54,10 +54,10 @@ public struct FastisShortcut<Value: FastisValue>: Hashable {
lhs.id == rhs.id
}

internal func isEqual(to value: Value) -> Bool {
if let date1 = self.action() as? Date, let date2 = value as? Date {
internal func isEqual(to value: Value, calendar: Calendar) -> Bool {
if let date1 = self.action(calendar) as? Date, let date2 = value as? Date {
return date1.isInSameDay(date: date2)
} else if let value1 = self.action() as? FastisRange, let value2 = value as? FastisRange {
} else if let value1 = self.action(calendar) as? FastisRange, let value2 = value as? FastisRange {
return value1 == value2
}
return false
Expand All @@ -69,26 +69,26 @@ public extension FastisShortcut where Value == FastisRange {

/// Range: from **`now.startOfDay`** to **`now.endOfDay`**
static var today: FastisShortcut {
FastisShortcut(name: "Today") {
FastisShortcut(name: "Today") { _ in
let now = Date()
return FastisRange(from: now.startOfDay(), to: now.endOfDay())
}
}

/// Range: from **`now.startOfDay - 7 days`** to **`now.endOfDay`**
static var lastWeek: FastisShortcut {
FastisShortcut(name: "Last week") {
FastisShortcut(name: "Last week") { calendar in
let now = Date()
let weekAgo = Calendar.current.date(byAdding: .day, value: -7, to: now)!
let weekAgo = calendar.date(byAdding: .day, value: -7, to: now)!
return FastisRange(from: weekAgo.startOfDay(), to: now.endOfDay())
}
}

/// Range: from **`now.startOfDay - 1 month`** to **`now.endOfDay`**
static var lastMonth: FastisShortcut {
FastisShortcut(name: "Last month") {
FastisShortcut(name: "Last month") { calendar in
let now = Date()
let monthAgo = Calendar.current.date(byAdding: .month, value: -1, to: now)!
let monthAgo = calendar.date(byAdding: .month, value: -1, to: now)!
return FastisRange(from: monthAgo.startOfDay(), to: now.endOfDay())
}
}
Expand All @@ -99,22 +99,22 @@ public extension FastisShortcut where Value == Date {

/// Date value: **`now`**
static var today: FastisShortcut {
FastisShortcut(name: "Today") {
FastisShortcut(name: "Today") { _ in
Date()
}
}

/// Date value: **`now - .day(1)`**
static var yesterday: FastisShortcut {
FastisShortcut(name: "Yesterday") {
Calendar.current.date(byAdding: .day, value: -1, to: Date())!
FastisShortcut(name: "Yesterday") { calendar in
calendar.date(byAdding: .day, value: -1, to: Date())!
}
}

/// Date value: **`now + .day(1)`**
static var tomorrow: FastisShortcut {
FastisShortcut(name: "Tomorrow") {
Calendar.current.date(byAdding: .day, value: 1, to: Date())!
FastisShortcut(name: "Tomorrow") { calendar in
calendar.date(byAdding: .day, value: 1, to: Date())!
}
}

Expand Down
28 changes: 16 additions & 12 deletions Sources/Views/Controller.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ open class FastisController<Value: FastisValue>: UIViewController, JTACMonthView
}()

private lazy var currentValueView: CurrentValueView<Value> = {
let view = CurrentValueView<Value>(config: self.config.currentValueView)
let view = CurrentValueView<Value>(
config: self.config.currentValueView,
calendar: self.config.calendar
)
view.currentValue = self.value
view.translatesAutoresizingMaskIntoConstraints = false
view.onClear = { [weak self] in
Expand All @@ -143,11 +146,13 @@ open class FastisController<Value: FastisValue>: UIViewController, JTACMonthView
)
view.translatesAutoresizingMaskIntoConstraints = false
if let value = self.value {
view.selectedShortcut = self.shortcuts.first(where: { $0.isEqual(to: value) })
view.selectedShortcut = self.shortcuts.first(where: {
$0.isEqual(to: value, calendar: self.config.calendar)
})
}
view.onSelect = { [weak self] selectedShortcut in
guard let self else { return }
let newValue = selectedShortcut.action()
let newValue = selectedShortcut.action(self.config.calendar)
if !newValue.outOfRange(minDate: self.privateMinimumDate, maxDate: self.privateMaximumDate) {
self.value = newValue
self.selectValue(newValue, in: self.calendarView)
Expand Down Expand Up @@ -266,6 +271,7 @@ open class FastisController<Value: FastisValue>: UIViewController, JTACMonthView
self.config = config
self.appearance = config.controller
self.dayFormatter.locale = config.calendar.locale
self.dayFormatter.calendar = config.calendar
self.dayFormatter.dateFormat = "d"
super.init(nibName: nil, bundle: nil)
}
Expand Down Expand Up @@ -418,7 +424,7 @@ open class FastisController<Value: FastisValue>: UIViewController, JTACMonthView
newConfig.dateLabelText = self.dayFormatter.string(from: date)
}

if Calendar.current.isDateInToday(date) {
if self.config.calendar.isDateInToday(date) {
newConfig.isToday = true
}

Expand All @@ -433,7 +439,9 @@ open class FastisController<Value: FastisValue>: UIViewController, JTACMonthView
private func updateSelectedShortcut() {
guard !self.shortcuts.isEmpty else { return }
if let value = self.value {
self.shortcutContainerView.selectedShortcut = self.shortcuts.first(where: { $0.isEqual(to: value) })
self.shortcutContainerView.selectedShortcut = self.shortcuts.first(where: {
$0.isEqual(to: value, calendar: self.config.calendar)
})
} else {
self.shortcutContainerView.selectedShortcut = nil
}
Expand Down Expand Up @@ -545,12 +553,8 @@ open class FastisController<Value: FastisValue>: UIViewController, JTACMonthView

public func configureCalendar(_ calendar: JTACMonthView) -> ConfigurationParameters {

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy MM dd"
dateFormatter.timeZone = self.config.calendar.timeZone
dateFormatter.locale = self.config.calendar.locale
var startDate = dateFormatter.date(from: "2000 01 01")!
var endDate = dateFormatter.date(from: "2030 12 01")!
var startDate = self.config.calendar.date(byAdding: .year, value: -99, to: Date())!
var endDate = self.config.calendar.date(byAdding: .year, value: 99, to: Date())!

if let maximumDate = self.privateMaximumDate,
let endOfNextMonth = self.config.calendar.date(byAdding: .month, value: 2, to: maximumDate)?
Expand Down Expand Up @@ -588,7 +592,7 @@ open class FastisController<Value: FastisValue>: UIViewController, JTACMonthView
withReuseIdentifier: self.monthHeaderReuseIdentifier,
for: indexPath
) as! MonthHeader
header.applyConfig(self.config.monthHeader)
header.applyConfig(self.config.monthHeader, calendar: self.config.calendar)
header.configure(for: range.start)
if self.privateSelectMonthOnHeaderTap, Value.mode == .range {
header.tapHandler = { [weak self, weak calendar] in
Expand Down
12 changes: 9 additions & 3 deletions Sources/Views/CurrentValueView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,16 @@ final class CurrentValueView<Value: FastisValue>: UIView {
// MARK: - Variables

private let config: FastisConfig.CurrentValueView
private let calendar: Calendar

/// Clear button tap handler
internal var onClear: (() -> Void)?

private lazy var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = self.config.locale
formatter.locale = self.calendar.locale
formatter.dateFormat = self.config.format
formatter.calendar = self.calendar
return formatter
}()

Expand All @@ -61,8 +63,9 @@ final class CurrentValueView<Value: FastisValue>: UIView {

// MARK: - Lifecycle

internal init(config: FastisConfig.CurrentValueView) {
internal init(config: FastisConfig.CurrentValueView, calendar: Calendar) {
self.config = config
self.calendar = calendar
super.init(frame: .zero)
self.configureUI()
self.configureSubviews()
Expand Down Expand Up @@ -235,6 +238,9 @@ public extension FastisConfig {

Default value — `Locale.autoupdatingCurrent`
*/
public var locale: Locale = .autoupdatingCurrent
@available(*, unavailable, message: "Use locale in FastisConfig.calendar.locale")
public var locale: Locale {
.autoupdatingCurrent
}
}
}
1 change: 0 additions & 1 deletion Sources/Views/DayCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ final class DayCell: JTACDayCell {

if let value = rangeValue {

let calendar = Calendar.current
var showRangeView = false

if state.dateBelongsTo == .followingMonthWithinBoundary {
Expand Down
16 changes: 10 additions & 6 deletions Sources/Views/MonthHeader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ final class MonthHeader: JTACMonthReusableView {
super.init(frame: frame)
self.configureSubviews()
self.configureConstraints()
self.applyConfig(FastisConfig.default.monthHeader)
self.applyConfig(FastisConfig.default.monthHeader, calendar: .current)
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.viewTapped))
self.addGestureRecognizer(tapRecognizer)
}
Expand Down Expand Up @@ -69,9 +69,10 @@ final class MonthHeader: JTACMonthReusableView {

// MARK: - Actions

internal func applyConfig(_ config: FastisConfig.MonthHeader) {
internal func applyConfig(_ config: FastisConfig.MonthHeader, calendar: Calendar) {
self.monthFormatter.calendar = calendar
self.monthFormatter.dateFormat = config.monthFormat
self.monthFormatter.locale = config.monthLocale
self.monthFormatter.locale = calendar.locale
self.monthLabel.font = config.labelFont
self.monthLabel.textColor = config.labelColor
self.monthLabel.textAlignment = config.labelAlignment
Expand Down Expand Up @@ -128,16 +129,19 @@ public extension FastisConfig {
/**
Format of displayed month value

Default value — `"LLLL yyyy"`
Default value — `"MMMM yyyy"`
*/
public var monthFormat = "LLLL yyyy"
public var monthFormat = "MMMM yyyy"

/**
Locale of displayed month value

Default value — `.current`
*/
public var monthLocale: Locale = .current
@available(*, unavailable, message: "Use locale FastisConfig.calendar.locale")
public var monthLocale: Locale {
.autoupdatingCurrent
}

/**
Height of month view
Expand Down
Loading

0 comments on commit 0e37d90

Please sign in to comment.