Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Fastis is a fully customisable UI component for picking dates and ranges created
- [Usage](#usage)
- [Quick Start](#quick-start)
- [Single and range modes](#single-and-range-modes)
- [Marked dates](#marked-dates)
- [Configuration](#configuration)
- [Shortcuts](#shortcuts)
- [Customization](#customization)
Expand Down Expand Up @@ -150,6 +151,57 @@ fastisController.dismissHandler = { [weak self] action in
}
```

### Marked dates

If you want to show a red dot under specific days, you can pass an array of dates to `markedDates`.
These dates are used only for display. They do not change the picker result type, so Fastis still works in single-date or range mode.

You can customize the marker globally through `FastisConfig.dayCell`:

```swift
FastisConfig.default.dayCell.markerColor = .systemGreen
FastisConfig.default.dayCell.markerSize = 6
```

UIKit:

```swift
let markedDates = [
Date(),
Calendar.current.date(byAdding: .day, value: 2, to: Date())!
]

let fastisController = FastisController(mode: .single)
fastisController.markedDates = markedDates
fastisController.dismissHandler = { action in
switch action {
case .done(let resultDate):
print(resultDate)
case .cancel:
break
}
}
```

SwiftUI:

```swift
let markedDates = [
Date(),
Calendar.current.date(byAdding: .day, value: 2, to: Date())!
]

FastisView(mode: .single) { action in
switch action {
case .done(let resultDate):
print(resultDate)
case .cancel:
break
}
}
.markedDates(markedDates)
```

### Configuration

FastisController has the following default configuration parameters:
Expand All @@ -159,6 +211,7 @@ var shortcuts: [FastisShortcut<Value>] = []
var allowsToChooseNilDate: Bool = false
var dismissHandler: ((DismissAction) -> Void)? = nil
var initialValue: Value? = nil
var markedDates: [Date] = []
var minimumDate: Date? = nil
var maximumDate: Date? = nil
var selectMonthOnHeaderTap: Bool = true
Expand All @@ -170,6 +223,7 @@ var closeOnSelectionImmediately: Bool = false
- `allowsToChooseNilDate`- Allow to choose `nil` date. If you set `true`, the done button will always be enabled and you will be able to reset selection by you tapping on selected date again. The default value is `false`.
- `dismissHandler`- The block to execute after the dismissal finishes. The default value is `nil`. Return DismissAction.done(FastisValue?) after the "Done" button will be tapped or DismissAction.cancel when controller dismissed without tapped the "Done" button.
- `initialValue`- And initial value which will be selected by default. The default value is `nil`.
- `markedDates`- Dates that should display a red marker dot in the calendar. The default value is `[]`.
- `minimumDate`- Minimal selection date. Dates less than current will be marked as unavailable. The default value is `nil`.
- `maximumDate`- Maximum selection date. Dates more significant than current will be marked as unavailable. The default value is `nil`.
- `selectMonthOnHeaderTap` (Only for `.range` mode) - Set this variable to `true` if you want to allow select date ranges by tapping on months. The default value is `true`.
Expand Down
55 changes: 50 additions & 5 deletions Sources/Views/DayCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ final class DayCell: JTACDayCell {
return view
}()

lazy var markerView: UIView = {
let view = UIView()
view.backgroundColor = .systemRed
view.isHidden = true
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

lazy var selectionBackgroundView: UIView = {
let view = UIView()
view.isHidden = true
Expand All @@ -52,6 +60,8 @@ final class DayCell: JTACDayCell {
private var rangeViewLeftAnchorToCenterConstraint: NSLayoutConstraint?
private var rangeViewRightAnchorToSuperviewConstraint: NSLayoutConstraint?
private var rangeViewRightAnchorToCenterConstraint: NSLayoutConstraint?
private var markerViewWidthConstraint: NSLayoutConstraint?
private var markerViewHeightConstraint: NSLayoutConstraint?

// MARK: - Lifecycle

Expand Down Expand Up @@ -88,6 +98,9 @@ final class DayCell: JTACDayCell {
self.selectionBackgroundView.backgroundColor = config.selectedBackgroundColor
self.dateLabel.font = config.dateLabelFont
self.dateLabel.textColor = config.dateLabelColor
self.markerView.backgroundColor = config.markerColor
self.markerViewWidthConstraint?.constant = config.markerSize
self.markerViewHeightConstraint?.constant = config.markerSize
if let cornerRadius = config.customSelectionViewCornerRadius {
self.selectionBackgroundView.layer.cornerRadius = cornerRadius
}
Expand All @@ -99,6 +112,7 @@ final class DayCell: JTACDayCell {
self.contentView.addSubview(self.backgroundRangeView)
self.contentView.addSubview(self.selectionBackgroundView)
self.contentView.addSubview(self.dateLabel)
self.contentView.addSubview(self.markerView)
self.selectionBackgroundView.layer.cornerRadius = min(self.frame.width, self.frame.height) / 2
}

Expand All @@ -109,6 +123,16 @@ final class DayCell: JTACDayCell {
self.dateLabel.centerYAnchor.constraint(equalTo: self.contentView.centerYAnchor)
])

self.markerViewWidthConstraint = self.markerView.widthAnchor.constraint(equalToConstant: self.config.markerSize)
self.markerViewHeightConstraint = self.markerView.heightAnchor.constraint(equalToConstant: self.config.markerSize)

NSLayoutConstraint.activate([
self.markerView.centerXAnchor.constraint(equalTo: self.dateLabel.centerXAnchor),
self.markerView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -6),
self.markerViewWidthConstraint,
self.markerViewHeightConstraint
].compactMap { $0 })

self.rangeViewLeftAnchorToSuperviewConstraint = self.backgroundRangeView.leftAnchor.constraint(equalTo: self.contentView.leftAnchor)
self.rangeViewLeftAnchorToCenterConstraint = self.backgroundRangeView.leftAnchor.constraint(equalTo: self.contentView.centerXAnchor)

Expand Down Expand Up @@ -278,13 +302,16 @@ final class DayCell: JTACDayCell {
var isDateEnabled = true
var rangeView = RangeViewConfig()
var isToday = false
var showsMarker = false
}

internal func configure(for config: ViewConfig) {

self.selectionBackgroundView.isHidden = config.isSelectedViewHidden
self.isUserInteractionEnabled = config.dateLabelText != nil && config.isDateEnabled
self.clipsToBounds = config.dateLabelText == nil
self.markerView.layer.cornerRadius = 2.5
self.markerView.isHidden = !config.showsMarker || config.dateLabelText == nil

if let dateLabelText = config.dateLabelText {
self.dateLabel.isHidden = false
Expand Down Expand Up @@ -367,6 +394,9 @@ final class DayCell: JTACDayCell {
private func configureTodayCell(viewConfig: ViewConfig, todayConfig: FastisConfig.TodayCell) {
self.dateLabel.font = todayConfig.dateLabelFont

let usesMarkerStyle = viewConfig.showsMarker
let circleSize = usesMarkerStyle ? self.config.markerSize : todayConfig.circleSize

if !viewConfig.isDateEnabled {
self.dateLabel.textColor = todayConfig.dateLabelUnavailableColor
self.circleView.backgroundColor = todayConfig.circleViewUnavailableColor
Expand All @@ -375,20 +405,21 @@ final class DayCell: JTACDayCell {
self.circleView.backgroundColor = todayConfig.circleViewSelectedColor
} else if !viewConfig.rangeView.isHidden {
self.dateLabel.textColor = todayConfig.onRangeLabelColor
self.circleView.backgroundColor = todayConfig.onRangeLabelColor
self.circleView.backgroundColor = usesMarkerStyle ? self.config.markerColor : todayConfig.circleViewOnRangeColor
} else {
self.dateLabel.textColor = todayConfig.dateLabelColor
self.circleView.backgroundColor = todayConfig.circleViewColor
self.circleView.backgroundColor = usesMarkerStyle ? self.config.markerColor : todayConfig.circleViewColor
}

self.circleView.layer.cornerRadius = todayConfig.circleSize * 0.5
self.markerView.isHidden = true
self.circleView.layer.cornerRadius = circleSize * 0.5
self.circleView.removeFromSuperview()
self.contentView.addSubview(self.circleView)
NSLayoutConstraint.activate([
self.circleView.centerXAnchor.constraint(equalTo: self.dateLabel.centerXAnchor),
self.circleView.topAnchor.constraint(equalTo: self.dateLabel.bottomAnchor, constant: todayConfig.circleVerticalInset),
self.circleView.widthAnchor.constraint(equalToConstant: todayConfig.circleSize),
self.circleView.heightAnchor.constraint(equalToConstant: todayConfig.circleSize)
self.circleView.widthAnchor.constraint(equalToConstant: circleSize),
self.circleView.heightAnchor.constraint(equalToConstant: circleSize)
])
}

Expand Down Expand Up @@ -431,6 +462,20 @@ public extension FastisConfig {
*/
public var selectedBackgroundColor: UIColor = .systemBlue

/**
Color of marker dot displayed under marked dates

Default value — `.systemRed`
*/
public var markerColor: UIColor = .systemRed

/**
Size of marker dot displayed under marked dates

Default value — `5pt`
*/
public var markerSize: CGFloat = 5

/**
Color of date label in cell when date is selected

Expand Down
21 changes: 21 additions & 0 deletions Sources/Views/FastisController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ open class FastisController<Value: FastisValue>: UIViewController, JTACMonthView
private var dayFormatter = DateFormatter()
private var isDone = false
private var privateCloseOnSelectionImmediately = false
private var markedDateSet = Set<Date>()

private var value: Value? {
didSet {
Expand Down Expand Up @@ -212,6 +213,23 @@ open class FastisController<Value: FastisValue>: UIViewController, JTACMonthView
*/
public var initialValue: Value?

/**
Dates that should display a red marker dot in the calendar.

Default value - `"[]"`
*/
public var markedDates: [Date] = [] {
didSet {
self.markedDateSet = Set(
self.markedDates.map { $0.startOfDay(in: self.config.calendar) }
)
self.viewConfigs.removeAll()
if self.isViewLoaded {
self.calendarView.reloadData()
}
}
}

/**
Minimal selection date. Dates less then current will be marked as unavailable

Expand Down Expand Up @@ -400,6 +418,9 @@ open class FastisController<Value: FastisValue>: UIViewController, JTACMonthView

if newConfig.dateLabelText != nil {
newConfig.dateLabelText = self.dayFormatter.string(from: date)
newConfig.showsMarker = self.markedDateSet.contains(
date.startOfDay(in: self.config.calendar)
)
}

if self.config.calendar.isDateInToday(date) {
Expand Down
10 changes: 10 additions & 0 deletions Sources/Views/FastisView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,16 @@ public struct FastisView<Value: FastisValue>: UIViewControllerRepresentable {
return self
}

/**
Dates that should display a red marker dot in the calendar.

Default value - `"[]"`
*/
public func markedDates(_ value: [Date]) -> Self {
self.controller.markedDates = value
return self
}

}

public extension FastisView where Value == FastisRange {
Expand Down