From e97362059708814e68d91feff74960560be7e73a Mon Sep 17 00:00:00 2001 From: UriyDevyataev Date: Thu, 14 Mar 2024 15:08:40 +0300 Subject: [PATCH 1/4] fix #48 FastisView for presentation in SwiftUI --- .../FastisExample.xcodeproj/project.pbxproj | 10 +- Example/Source/HostingController.swift | 24 +++ Example/Source/MainView.swift | 112 ++++++++++++ Example/Source/ViewController.swift | 26 ++- Sources/Views/FastisView.swift | 161 ++++++++++++++++++ 5 files changed, 327 insertions(+), 6 deletions(-) create mode 100644 Example/Source/HostingController.swift create mode 100644 Example/Source/MainView.swift create mode 100644 Sources/Views/FastisView.swift diff --git a/Example/FastisExample.xcodeproj/project.pbxproj b/Example/FastisExample.xcodeproj/project.pbxproj index 92eff59..d88b34a 100644 --- a/Example/FastisExample.xcodeproj/project.pbxproj +++ b/Example/FastisExample.xcodeproj/project.pbxproj @@ -3,11 +3,13 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 1A3E7A5E2A40901600434229 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1A3E7A602A40901600434229 /* Localizable.strings */; }; + 1AB955832BA31F4200235243 /* HostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB955822BA31F4200235243 /* HostingController.swift */; }; + 1AB955852BA31F5500235243 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB955842BA31F5500235243 /* MainView.swift */; }; F381AF9628B8C7190046383A /* Fastis in Frameworks */ = {isa = PBXBuildFile; productRef = F381AF9528B8C7190046383A /* Fastis */; }; F3FCEE47244780FE000F966E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3FCEE46244780FE000F966E /* AppDelegate.swift */; }; F3FCEE4B244780FE000F966E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3FCEE4A244780FE000F966E /* ViewController.swift */; }; @@ -19,6 +21,8 @@ 1A3E7A5F2A40901600434229 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 1A3E7A612A40902000434229 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = ""; }; 1A3E7A622A409B3A00434229 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + 1AB955822BA31F4200235243 /* HostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostingController.swift; sourceTree = ""; }; + 1AB955842BA31F5500235243 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; CB137E4877EBDC6E87A77A4B /* Pods_FastisExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FastisExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F31CFB6B28B8C32D00364F6A /* Fastis */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Fastis; path = ..; sourceTree = ""; }; F3FCEE43244780FE000F966E /* FastisExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FastisExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -80,6 +84,8 @@ children = ( F3FCEE46244780FE000F966E /* AppDelegate.swift */, F3FCEE4A244780FE000F966E /* ViewController.swift */, + 1AB955822BA31F4200235243 /* HostingController.swift */, + 1AB955842BA31F5500235243 /* MainView.swift */, F3FCEE4F24478100000F966E /* Assets.xcassets */, F3FCEE5124478100000F966E /* LaunchScreen.storyboard */, F3FCEE5424478100000F966E /* Info.plist */, @@ -164,6 +170,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1AB955832BA31F4200235243 /* HostingController.swift in Sources */, + 1AB955852BA31F5500235243 /* MainView.swift in Sources */, F3FCEE4B244780FE000F966E /* ViewController.swift in Sources */, F3FCEE47244780FE000F966E /* AppDelegate.swift in Sources */, ); diff --git a/Example/Source/HostingController.swift b/Example/Source/HostingController.swift new file mode 100644 index 0000000..69c5f41 --- /dev/null +++ b/Example/Source/HostingController.swift @@ -0,0 +1,24 @@ +// +// HostingController.swift +// FastisExample +// +// Created by Yriy Devyataev on 13.03.2024. +// Copyright © 2024 RetailDriver LLC. All rights reserved. +// + +import SwiftUI + +/** + The view is used to display SwiftUI view in the UIKit project + */ + +class HostingController: UIHostingController { + + init() { + super.init(rootView: MainView()) + } + + @MainActor required dynamic init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/Example/Source/MainView.swift b/Example/Source/MainView.swift new file mode 100644 index 0000000..b5417cb --- /dev/null +++ b/Example/Source/MainView.swift @@ -0,0 +1,112 @@ +// +// CalendarView.swift +// FastisExample +// +// Created by Yriy Devyataev on 13.03.2024. +// Copyright © 2024 RetailDriver LLC. All rights reserved. +// + + +import Foundation +import SwiftUI +import UIKit +import Fastis + +/** + View is used as a possible example of a SwiftUI project. + */ + +struct MainView: View { + + @State private var showSingleCalendar = false + @State private var showRangeCalendar = false + @State private var currentValueText: String = "Choose a date" + + @State var currentValue: FastisValue? { + didSet { + if let rangeValue = self.currentValue as? FastisRange { + self.currentValueText = self.dateFormatter.string(from: rangeValue.fromDate) + " - " + self.dateFormatter.string(from: rangeValue.toDate) + } else if let date = self.currentValue as? Date { + self.currentValueText = self.dateFormatter.string(from: date) + } else { + self.currentValueText = "Choose a date" + } + } + } + + @State private var dateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateFormat = "dd/MM/yyyy" + return formatter + }() + + var body: some View { + VStack(spacing: 32, content: { + Text(self.currentValueText) + VStack(alignment: .center, spacing: 16, content: { + Button("Choose range of dates") { + self.showRangeCalendar.toggle() + } + Button("Choose single date") { + self.showSingleCalendar.toggle() + } + }) + }) + .navigationTitle("SwiftUI presentation") + .frame(maxWidth: .infinity, maxHeight: .infinity) + .sheet(isPresented: $showRangeCalendar) { + self.rangeCalendarView() + } + .sheet(isPresented: $showSingleCalendar) { + self.singleCalendarView() + } + } + + private func rangeCalendarView() -> some View { + let config: FastisConfig = .default + var fastisView = FastisView(mode: .range, config: config) + fastisView.title = "Choose range" + fastisView.initialValue = self.currentValue as? FastisRange + fastisView.minimumDate = Calendar.current.date(byAdding: .month, value: -2, to: Date()) + fastisView.maximumDate = Calendar.current.date(byAdding: .month, value: 3, to: Date()) + fastisView.allowToChooseNilDate = true + fastisView.allowDateRangeChanges = false + fastisView.shortcuts = [.lastWeek, .lastMonth] + fastisView.selectMonthOnHeaderTap = true + + fastisView.dismissHandler = { action in + switch action { + case .done(let newValue): + self.currentValue = newValue + case .cancel: + print("any actions") + } + } + return fastisView.ignoresSafeArea() + } + + private func singleCalendarView() -> some View { + let config: FastisConfig = .default + var fastisView = FastisView(mode: .single, config: config) + fastisView.title = "Choose date" + fastisView.initialValue = self.currentValue as? Date + fastisView.minimumDate = Calendar.current.date(byAdding: .month, value: -2, to: Date()) + fastisView.maximumDate = Date() + fastisView.allowToChooseNilDate = true + fastisView.allowDateRangeChanges = false + fastisView.shortcuts = [.yesterday, .today, .tomorrow] + fastisView.closeOnSelectionImmediately = true + + fastisView.dismissHandler = { action in + switch action { + case .done(let newValue): + self.currentValue = newValue + case .cancel: + print("any actions") + } + } + return fastisView.ignoresSafeArea() + } + +} + diff --git a/Example/Source/ViewController.swift b/Example/Source/ViewController.swift index 5049ad5..df52074 100644 --- a/Example/Source/ViewController.swift +++ b/Example/Source/ViewController.swift @@ -40,10 +40,17 @@ class ViewController: UIViewController { return button }() - private lazy var chooseSingleButtonWithCustomCalendar: UIButton = { + private lazy var chooseRangeButtonWithCustomCalendar: 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) + button.setTitle("Choose range of dates with custom calendar", for: .normal) + button.addTarget(self, action: #selector(self.chooseRangeWithCustomCalendar), for: .touchUpInside) + return button + }() + + private lazy var chooseWithSwiftUI: UIButton = { + let button = UIButton(type: .system) + button.setTitle("Choose with SwiftUI", for: .normal) + button.addTarget(self, action: #selector(self.swiftUIPresentation), for: .touchUpInside) return button }() @@ -91,7 +98,8 @@ 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.containerView.addArrangedSubview(self.chooseRangeButtonWithCustomCalendar) + self.containerView.addArrangedSubview(self.chooseWithSwiftUI) self.view.addSubview(self.containerView) } @@ -149,7 +157,7 @@ class ViewController: UIViewController { } @objc - private func chooseSingleDateWithCustomCalendar() { + private func chooseRangeWithCustomCalendar() { var customConfig: FastisConfig = .default var calendar: Calendar = .init(identifier: .islamicUmmAlQura) calendar.locale = .autoupdatingCurrent @@ -175,4 +183,12 @@ class ViewController: UIViewController { fastisController.present(above: self) } + @objc + private func swiftUIPresentation() { + let hostingController = HostingController() + hostingController.modalPresentationStyle = .custom + let navVC = self.parent as? UINavigationController + navVC?.pushViewController(hostingController, animated: true) + } + } diff --git a/Sources/Views/FastisView.swift b/Sources/Views/FastisView.swift new file mode 100644 index 0000000..e172819 --- /dev/null +++ b/Sources/Views/FastisView.swift @@ -0,0 +1,161 @@ +// +// FastisView.swift +// FastisExample +// +// Created by Yriy Devyataev on 13.03.2024. +// Copyright © 2024 RetailDriver LLC. All rights reserved. +// + +import SwiftUI + +/** +View of Fastis framework. Use it to create and present dade picker + + Usage example: + ```swift + let fastisView = FastisView(mode: .range) + fastisView.title = "Choose range" + fastisView.maximumDate = Date() + fastisView.allowToChooseNilDate = true + fastisView.shortcuts = [.today, .lastWeek] + fastisView.dismissHandler = { [weak self] action in + switch action { + case .done(let newValue): + ... + case .cancel: + ... + } + } + ``` + + **Single and range modes** + + If you want to get a single date you have to use `Date` type: + + ```swift + let fastisView = FastisView(mode: .single) + fastisView.initialValue = Date() + fastisView.closeOnSelectionImmediately = true + fastisView.dismissHandler = { [weak self] action in + switch action { + case .done(let resultDate): + print(resultDate) // resultDate is Date + case .cancel: + ... + } + } + ``` + + If you want to get a date range you have to use `FastisRange` type: + + ```swift + let fastisView = FastisView(mode: .range) + fastisView.initialValue = FastisRange(from: Date(), to: Date()) // or .from(Date(), to: Date()) + fastisView.dismissHandler = { [weak self] action in + switch action { + case .done(let resultRange): + print(resultRange) // resultRange is FastisRange + case .cancel: + ... + } + } + ``` + */ + +public struct FastisView: UIViewControllerRepresentable { + + public var title: String? = nil + public var initialValue: Value? = nil + public var minimumDate: Date? = nil + public var maximumDate: Date? = nil + public var allowToChooseNilDate: Bool = false + public var allowDateRangeChanges: Bool = true + public var shortcuts: [FastisShortcut] = [] + public var dismissHandler: ((FastisController.DismissAction) -> Void)? = nil + + private var privateSelectMonthOnHeaderTap = false + private var privateCloseOnSelectionImmediately = false + + private let config: FastisConfig + + /// Initiate FastisView + /// - Parameter config: Configuration parameters + init(config: FastisConfig = .default) { + self.config = config + } + + public func makeUIViewController(context: Context) -> UINavigationController { + let fastisController = FastisController(config: self.config) + fastisController.title = self.title + fastisController.initialValue = self.initialValue + fastisController.minimumDate = self.minimumDate + fastisController.maximumDate = self.maximumDate + fastisController.allowToChooseNilDate = self.allowToChooseNilDate + fastisController.allowDateRangeChanges = self.allowDateRangeChanges + fastisController.shortcuts = self.shortcuts + fastisController.dismissHandler = self.dismissHandler + switch Value.mode { + case .single: + let singleController = fastisController as? FastisController + singleController?.closeOnSelectionImmediately = self.privateCloseOnSelectionImmediately + case .range: + let rangeController = fastisController as? FastisController + rangeController?.selectMonthOnHeaderTap = self.privateSelectMonthOnHeaderTap + } + return UINavigationController(rootViewController: fastisController) + } + + public func updateUIViewController( + _ uiViewController: UINavigationController, + context: UIViewControllerRepresentableContext + ) {} +} + +public extension FastisView where Value == FastisRange { + + /// Initiate FastisView + /// - Parameters: + /// - mode: Choose `.range` or `.single` mode + /// - config: Custom configuration parameters. Default value is equal to `FastisConfig.default` + init(mode: FastisModeRange, config: FastisConfig = .default) { + self.init(config: config) + } + + /** + Set this variable to `true` if you want to allow select date ranges by tapping on months + */ + var selectMonthOnHeaderTap: Bool { + get { + self.privateSelectMonthOnHeaderTap + } + set { + self.privateSelectMonthOnHeaderTap = newValue + } + } +} + +public extension FastisView where Value == Date { + + /// Initiate FastisView + /// - Parameters: + /// - mode: Choose .range or .single mode + /// - config: Custom configuration parameters. Default value is equal to `FastisConfig.default` + init(mode: FastisModeSingle, config: FastisConfig = .default) { + self.init(config: config) + } + + /** + Set this variable to `true` if you want to hide view of the selected date and close the controller right after the date is selected. + + Default value — `"False"` + */ + var closeOnSelectionImmediately: Bool { + get { + self.privateCloseOnSelectionImmediately + } + set { + self.privateCloseOnSelectionImmediately = newValue + } + } +} + From dda7a45b3aa2fe51671c49dbeddf454893e62b14 Mon Sep 17 00:00:00 2001 From: UriyDevyataev Date: Thu, 14 Mar 2024 18:15:04 +0300 Subject: [PATCH 2/4] fix #48 add modifiers --- Example/Source/MainView.swift | 82 +++++------- Sources/Views/FastisView.swift | 221 ++++++++++++++++++++++++++------- 2 files changed, 209 insertions(+), 94 deletions(-) diff --git a/Example/Source/MainView.swift b/Example/Source/MainView.swift index b5417cb..419b917 100644 --- a/Example/Source/MainView.swift +++ b/Example/Source/MainView.swift @@ -55,58 +55,42 @@ struct MainView: View { .navigationTitle("SwiftUI presentation") .frame(maxWidth: .infinity, maxHeight: .infinity) .sheet(isPresented: $showRangeCalendar) { - self.rangeCalendarView() - } - .sheet(isPresented: $showSingleCalendar) { - self.singleCalendarView() - } - } - - private func rangeCalendarView() -> some View { - let config: FastisConfig = .default - var fastisView = FastisView(mode: .range, config: config) - fastisView.title = "Choose range" - fastisView.initialValue = self.currentValue as? FastisRange - fastisView.minimumDate = Calendar.current.date(byAdding: .month, value: -2, to: Date()) - fastisView.maximumDate = Calendar.current.date(byAdding: .month, value: 3, to: Date()) - fastisView.allowToChooseNilDate = true - fastisView.allowDateRangeChanges = false - fastisView.shortcuts = [.lastWeek, .lastMonth] - fastisView.selectMonthOnHeaderTap = true - - fastisView.dismissHandler = { action in - switch action { - case .done(let newValue): - self.currentValue = newValue - case .cancel: - print("any actions") + FastisView(mode: .range) { action in + switch action { + case .done(let newValue): + self.currentValue = newValue + case .cancel: + print("any actions") + } } + .title("Choose range") + .initialValue(self.currentValue as? FastisRange) + .minimumDate(Calendar.current.date(byAdding: .month, value: -2, to: Date())) + .maximumDate(Calendar.current.date(byAdding: .month, value: 3, to: Date())) + .allowToChooseNilDate(true) + .allowDateRangeChanges(false) + .shortcuts([.lastWeek, .lastMonth]) + .selectMonthOnHeaderTap(true) + .ignoresSafeArea() } - return fastisView.ignoresSafeArea() - } - - private func singleCalendarView() -> some View { - let config: FastisConfig = .default - var fastisView = FastisView(mode: .single, config: config) - fastisView.title = "Choose date" - fastisView.initialValue = self.currentValue as? Date - fastisView.minimumDate = Calendar.current.date(byAdding: .month, value: -2, to: Date()) - fastisView.maximumDate = Date() - fastisView.allowToChooseNilDate = true - fastisView.allowDateRangeChanges = false - fastisView.shortcuts = [.yesterday, .today, .tomorrow] - fastisView.closeOnSelectionImmediately = true - - fastisView.dismissHandler = { action in - switch action { - case .done(let newValue): - self.currentValue = newValue - case .cancel: - print("any actions") + .sheet(isPresented: $showSingleCalendar) { + FastisView(mode: .single) { action in + switch action { + case .done(let newValue): + self.currentValue = newValue + case .cancel: + print("any actions") + } } + .title("Choose date") + .initialValue(self.currentValue as? Date) + .minimumDate(Calendar.current.date(byAdding: .month, value: -2, to: Date())) + .maximumDate(Date()) + .allowToChooseNilDate(true) + .allowDateRangeChanges(false) + .shortcuts([.yesterday, .today, .tomorrow]) + .closeOnSelectionImmediately(true) + .ignoresSafeArea() } - return fastisView.ignoresSafeArea() } - } - diff --git a/Sources/Views/FastisView.swift b/Sources/Views/FastisView.swift index e172819..532139c 100644 --- a/Sources/Views/FastisView.swift +++ b/Sources/Views/FastisView.swift @@ -12,76 +12,128 @@ import SwiftUI View of Fastis framework. Use it to create and present dade picker Usage example: - ```swift - let fastisView = FastisView(mode: .range) - fastisView.title = "Choose range" - fastisView.maximumDate = Date() - fastisView.allowToChooseNilDate = true - fastisView.shortcuts = [.today, .lastWeek] - fastisView.dismissHandler = { [weak self] action in - switch action { - case .done(let newValue): - ... - case .cancel: - ... - } + ```swiftUI + FastisView(mode: .range) { action in + switch action { + case .done(let newValue): + ... + case .cancel: + ... + } } + .title = "Choose range" + .maximumDate = Date() + .allowToChooseNilDate = true + .shortcuts = [.today, .lastWeek] ``` **Single and range modes** If you want to get a single date you have to use `Date` type: - ```swift - let fastisView = FastisView(mode: .single) - fastisView.initialValue = Date() - fastisView.closeOnSelectionImmediately = true - fastisView.dismissHandler = { [weak self] action in - switch action { - case .done(let resultDate): - print(resultDate) // resultDate is Date - case .cancel: - ... - } - } + ```swiftUI +FastisView(mode: .single) { action in + switch action { + case .done(let resultDate): + print(resultDate) // resultDate is Date + case .cancel: + ... + } +} +.initialValue = Date() +.closeOnSelectionImmediately = true ``` If you want to get a date range you have to use `FastisRange` type: - ```swift - let fastisView = FastisView(mode: .range) - fastisView.initialValue = FastisRange(from: Date(), to: Date()) // or .from(Date(), to: Date()) - fastisView.dismissHandler = { [weak self] action in - switch action { - case .done(let resultRange): - print(resultRange) // resultRange is FastisRange - case .cancel: - ... - } + ```swiftUI +FastisView(mode: .range) { action in + switch action { + case .done(let resultRange): + print(resultRange) // resultRange is FastisRange + case .cancel: + ... + } } +.initialValue = FastisRange(from: Date(), to: Date()) // or .from(Date(), to: Date()) ``` */ public struct FastisView: UIViewControllerRepresentable { + private var privateSelectMonthOnHeaderTap = false + private var privateCloseOnSelectionImmediately = false + private let config: FastisConfig + + /** + And title controller + */ public var title: String? = nil + + /** + And initial value which will be selected by default + */ public var initialValue: Value? = nil + + /** + Minimal selection date. Dates less then current will be marked as unavailable + */ public var minimumDate: Date? = nil + + /** + Maximum selection date. Dates greater then current will be marked as unavailable + */ public var maximumDate: Date? = nil + + /** + Allow to choose `nil` date + + When `allowToChooseNilDate` is `true`: + * "Done" button will be always enabled + * You will be able to reset selection by you tapping on selected date again + */ public var allowToChooseNilDate: Bool = false + + /** + Allow date range changes + + Set this variable to `false` if you want to disable date range changes. + Next tap after selecting range will start new range selection. + */ public var allowDateRangeChanges: Bool = true - public var shortcuts: [FastisShortcut] = [] - public var dismissHandler: ((FastisController.DismissAction) -> Void)? = nil - private var privateSelectMonthOnHeaderTap = false - private var privateCloseOnSelectionImmediately = false + /** + Shortcuts array - private let config: FastisConfig + You can use prepared shortcuts depending on the current mode. + + - For `.single` mode: `.today`, `.tomorrow`, `.yesterday` + - For `.range` mode: `.today`, `.lastWeek`, `.lastMonth` + + Or you can create your own shortcuts: + + ``` + var customShortcut = FastisShortcut(name: "Today") { + let now = Date() + return FastisRange(from: now.startOfDay(), to: now.endOfDay()) + } + ``` + */ + public var shortcuts: [FastisShortcut] = [] + + /** + The block to execute after the dismissal finishes, return two variable .done(FastisValue?) and .cancel + */ + public var dismissHandler: ((FastisController.DismissAction) -> Void)? = nil /// Initiate FastisView /// - Parameter config: Configuration parameters - init(config: FastisConfig = .default) { + public init( + config: FastisConfig = .default, + dismissHandler: ((FastisController.DismissAction) -> Void)? = nil + ) { self.config = config + self.dismissHandler = dismissHandler } public func makeUIViewController(context: Context) -> UINavigationController { @@ -117,8 +169,12 @@ public extension FastisView where Value == FastisRange { /// - Parameters: /// - mode: Choose `.range` or `.single` mode /// - config: Custom configuration parameters. Default value is equal to `FastisConfig.default` - init(mode: FastisModeRange, config: FastisConfig = .default) { - self.init(config: config) + init( + mode: FastisModeRange, + config: FastisConfig = .default, + dismissHandler: ((FastisController.DismissAction) -> Void)? = nil + ) { + self.init(config: config, dismissHandler: dismissHandler) } /** @@ -140,8 +196,12 @@ public extension FastisView where Value == Date { /// - Parameters: /// - mode: Choose .range or .single mode /// - config: Custom configuration parameters. Default value is equal to `FastisConfig.default` - init(mode: FastisModeSingle, config: FastisConfig = .default) { - self.init(config: config) + init( + mode: FastisModeSingle, + config: FastisConfig = .default, + dismissHandler: ((FastisController.DismissAction) -> Void)? = nil + ) { + self.init(config: config, dismissHandler: dismissHandler) } /** @@ -159,3 +219,74 @@ public extension FastisView where Value == Date { } } +public extension FastisView { + + /// Modifier for property 'title' + func title(_ value: String?) -> FastisView { + var view = self + view.title = value + return view + } + + /// Modifier for property 'initialValue' + func initialValue(_ value: Value?) -> FastisView { + var view = self + view.initialValue = value + return view + } + + /// Modifier for property 'minimumDate' + func minimumDate(_ value: Date?) -> FastisView { + var view = self + view.minimumDate = value + return view + } + + /// Modifier for property 'maximumDate' + func maximumDate(_ value: Date?) -> FastisView { + var view = self + view.maximumDate = value + return view + } + + /// Modifier for property 'allowToChooseNilDate' + func allowToChooseNilDate(_ value: Bool) -> FastisView { + var view = self + view.allowToChooseNilDate = value + return view + } + + /// Modifier for property 'allowDateRangeChanges' + func allowDateRangeChanges(_ value: Bool) -> FastisView { + var view = self + view.allowDateRangeChanges = value + return view + } + + /// Modifier for property 'shortcuts' + func shortcuts(_ value: [FastisShortcut]) -> FastisView { + var view = self + view.shortcuts = value + return view + } +} + +public extension FastisView where Value == FastisRange { + + /// Modifier for property 'selectMonthOnHeaderTap' + func selectMonthOnHeaderTap(_ value: Bool) -> FastisView { + var view = self + view.selectMonthOnHeaderTap = value + return view + } +} + +public extension FastisView where Value == Date { + + /// Modifier for property 'closeOnSelectionImmediately' + func closeOnSelectionImmediately(_ value: Bool) -> FastisView { + var view = self + view.closeOnSelectionImmediately = value + return view + } +} From bea90080dc1df390327208c0998586291ab58ddf Mon Sep 17 00:00:00 2001 From: UriyDevyataev Date: Fri, 15 Mar 2024 11:57:03 +0300 Subject: [PATCH 3/4] fix #48 add swiftUI info in ReadMe --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/README.md b/README.md index 4ac156c..93ed313 100644 --- a/README.md +++ b/README.md @@ -246,6 +246,58 @@ config.todayCell.circleViewColor = .red If you don't want to customzie today date cell, just set `config.todayCell = nil` and today cell will use `dayCell` config. +### SwiftUI + +The library also contains a SwiftUI wrapper + +If you want to get a date range: + +```swiftUI + +FastisView(mode: .range) { action in + switch action { + case .done(let newValue): + self.currentValue = newValue + case .cancel: + print("any actions") + } +} +.title("Choose range") +.initialValue(self.currentValue as? FastisRange) +.minimumDate(Calendar.current.date(byAdding: .month, value: -2, to: Date())) +.maximumDate(Calendar.current.date(byAdding: .month, value: 3, to: Date())) +.allowToChooseNilDate(true) +.allowDateRangeChanges(false) +.shortcuts([.lastWeek, .lastMonth]) +.selectMonthOnHeaderTap(true) +.ignoresSafeArea() + +``` + +If you want to get a single date: + +```swiftUI + +FastisView(mode: .single) { action in + switch action { + case .done(let newValue): + self.currentValue = newValue + case .cancel: + print("any actions") + } +} +.title("Choose date") +.initialValue(self.currentValue as? Date) +.minimumDate(Calendar.current.date(byAdding: .month, value: -2, to: Date())) +.maximumDate(Date()) +.allowToChooseNilDate(true) +.allowDateRangeChanges(false) +.shortcuts([.yesterday, .today, .tomorrow]) +.closeOnSelectionImmediately(true) +.ignoresSafeArea() + +``` + ## Credits - Ilya Kharlamov ([@ilia3546](https://github.com/ilia3546)) From 335a3ec7c5d066bf411008cd9eae4ee78732e9b6 Mon Sep 17 00:00:00 2001 From: Ilya Kharlamov Date: Fri, 15 Mar 2024 14:54:45 +0300 Subject: [PATCH 4/4] Clean up --- Example/Source/HostingController.swift | 4 +- Example/Source/MainView.swift | 37 +- README.md | 45 ++- ...ontroller.swift => FastisController.swift} | 53 ++- Sources/Views/FastisView.swift | 322 +++++++----------- 5 files changed, 207 insertions(+), 254 deletions(-) rename Sources/Views/{Controller.swift => FastisController.swift} (97%) diff --git a/Example/Source/HostingController.swift b/Example/Source/HostingController.swift index 69c5f41..c0fb25a 100644 --- a/Example/Source/HostingController.swift +++ b/Example/Source/HostingController.swift @@ -18,7 +18,9 @@ class HostingController: UIHostingController { super.init(rootView: MainView()) } - @MainActor required dynamic init?(coder aDecoder: NSCoder) { + @available(*, unavailable) + dynamic required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + } diff --git a/Example/Source/MainView.swift b/Example/Source/MainView.swift index 419b917..2e322aa 100644 --- a/Example/Source/MainView.swift +++ b/Example/Source/MainView.swift @@ -6,26 +6,27 @@ // Copyright © 2024 RetailDriver LLC. All rights reserved. // - +import Fastis import Foundation import SwiftUI import UIKit -import Fastis /** View is used as a possible example of a SwiftUI project. */ - struct MainView: View { + private let calendar: Calendar = .current + @State private var showSingleCalendar = false @State private var showRangeCalendar = false - @State private var currentValueText: String = "Choose a date" + @State private var currentValueText = "Choose a date" @State var currentValue: FastisValue? { didSet { if let rangeValue = self.currentValue as? FastisRange { - self.currentValueText = self.dateFormatter.string(from: rangeValue.fromDate) + " - " + self.dateFormatter.string(from: rangeValue.toDate) + self.currentValueText = self.dateFormatter.string(from: rangeValue.fromDate) + " - " + self.dateFormatter + .string(from: rangeValue.toDate) } else if let date = self.currentValue as? Date { self.currentValueText = self.dateFormatter.string(from: date) } else { @@ -34,11 +35,12 @@ struct MainView: View { } } - @State private var dateFormatter: DateFormatter = { + private var dateFormatter: DateFormatter { let formatter = DateFormatter() formatter.dateFormat = "dd/MM/yyyy" + formatter.calendar = self.calendar return formatter - }() + } var body: some View { VStack(spacing: 32, content: { @@ -54,40 +56,33 @@ struct MainView: View { }) .navigationTitle("SwiftUI presentation") .frame(maxWidth: .infinity, maxHeight: .infinity) - .sheet(isPresented: $showRangeCalendar) { + .sheet(isPresented: self.$showRangeCalendar) { FastisView(mode: .range) { action in - switch action { - case .done(let newValue): + if case .done(let newValue) = action { self.currentValue = newValue - case .cancel: - print("any actions") } } .title("Choose range") .initialValue(self.currentValue as? FastisRange) - .minimumDate(Calendar.current.date(byAdding: .month, value: -2, to: Date())) - .maximumDate(Calendar.current.date(byAdding: .month, value: 3, to: Date())) + .minimumDate(self.calendar.date(byAdding: .month, value: -2, to: Date())) + .maximumDate(self.calendar.date(byAdding: .month, value: 3, to: Date())) .allowToChooseNilDate(true) .allowDateRangeChanges(false) .shortcuts([.lastWeek, .lastMonth]) .selectMonthOnHeaderTap(true) .ignoresSafeArea() } - .sheet(isPresented: $showSingleCalendar) { + .sheet(isPresented: self.$showSingleCalendar) { FastisView(mode: .single) { action in - switch action { - case .done(let newValue): + if case .done(let newValue) = action { self.currentValue = newValue - case .cancel: - print("any actions") } } .title("Choose date") .initialValue(self.currentValue as? Date) - .minimumDate(Calendar.current.date(byAdding: .month, value: -2, to: Date())) + .minimumDate(self.calendar.date(byAdding: .month, value: -2, to: Date())) .maximumDate(Date()) .allowToChooseNilDate(true) - .allowDateRangeChanges(false) .shortcuts([.yesterday, .today, .tomorrow]) .closeOnSelectionImmediately(true) .ignoresSafeArea() diff --git a/README.md b/README.md index 93ed313..804187e 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Fastis is a fully customisable UI component for picking dates and ranges created - [Configuration](#configuration) - [Shortcuts](#shortcuts) - [Customization](#customization) + - [SwiftUI](#swiftui) - [Credits](#credits) - [License](#license) @@ -161,7 +162,6 @@ var maximumDate: Date? = nil var selectMonthOnHeaderTap: Bool = true var allowDateRangeChanges: Bool = true var closeOnSelectionImmediately: Bool = false - ``` - `shortcuts`- Shortcuts array. The default value is `[]`. See [Shortcuts](#shortcuts) section @@ -226,7 +226,7 @@ To customise a special FastisController instance with custom calendar: ```swift var customConfig: FastisConfig = .default -var calendar: Calendar = .init(identifier: .islamicUmmAlQura) +var calendar = Calendar(identifier: .islamicUmmAlQura) calendar.locale = .autoupdatingCurrent customConfig.calendar = calendar let fastisController = FastisController(mode: .range, config: customConfig) @@ -252,16 +252,15 @@ The library also contains a SwiftUI wrapper If you want to get a date range: -```swiftUI - -FastisView(mode: .range) { action in - switch action { - case .done(let newValue): - self.currentValue = newValue - case .cancel: - print("any actions") +```swift +FastisView(mode: .single, dismissHandler: { action in + switch action { + case .done(let resultDate): + print(resultDate) // resultDate is Date + case .cancel: + ... } -} +}) .title("Choose range") .initialValue(self.currentValue as? FastisRange) .minimumDate(Calendar.current.date(byAdding: .month, value: -2, to: Date())) @@ -270,22 +269,19 @@ FastisView(mode: .range) { action in .allowDateRangeChanges(false) .shortcuts([.lastWeek, .lastMonth]) .selectMonthOnHeaderTap(true) -.ignoresSafeArea() - ``` If you want to get a single date: -```swiftUI - -FastisView(mode: .single) { action in - switch action { - case .done(let newValue): - self.currentValue = newValue - case .cancel: - print("any actions") - } -} +```swift +FastisView(mode: .range, dismissHandler: { action in + switch action { + case .done(let resultRange): + print(resultRange) // resultRange is FastisRange + case .cancel: + ... + } +}) .title("Choose date") .initialValue(self.currentValue as? Date) .minimumDate(Calendar.current.date(byAdding: .month, value: -2, to: Date())) @@ -294,13 +290,12 @@ FastisView(mode: .single) { action in .allowDateRangeChanges(false) .shortcuts([.yesterday, .today, .tomorrow]) .closeOnSelectionImmediately(true) -.ignoresSafeArea() - ``` ## Credits - Ilya Kharlamov ([@ilia3546](https://github.com/ilia3546)) +- Uriy Devyataev ([@UriyDevyataev](https://github.com/UriyDevyataev)) ## License diff --git a/Sources/Views/Controller.swift b/Sources/Views/FastisController.swift similarity index 97% rename from Sources/Views/Controller.swift rename to Sources/Views/FastisController.swift index b2047cb..6777063 100644 --- a/Sources/Views/Controller.swift +++ b/Sources/Views/FastisController.swift @@ -170,8 +170,12 @@ open class FastisController: UIViewController, JTACMonthView private var viewConfigs: [IndexPath: DayCell.ViewConfig] = [:] private var privateMinimumDate: Date? private var privateMaximumDate: Date? + private var privateAllowDateRangeChanges = true private var privateSelectMonthOnHeaderTap = false private var dayFormatter = DateFormatter() + private var isDone = false + private var privateCloseOnSelectionImmediately = false + private var value: Value? { didSet { self.updateSelectedShortcut() @@ -180,12 +184,11 @@ open class FastisController: UIViewController, JTACMonthView } } - private var isDone = false - private var privateCloseOnSelectionImmediately = false - /** Shortcuts array + Default value — `"[]"` + You can use prepared shortcuts depending on the current mode. - For `.single` mode: `.today`, `.tomorrow`, `.yesterday` @@ -208,11 +211,15 @@ open class FastisController: UIViewController, JTACMonthView When `allowToChooseNilDate` is `true`: * "Done" button will be always enabled * You will be able to reset selection by you tapping on selected date again + + Default value — `"false"` */ public var allowToChooseNilDate = false /** - The block to execute after the dismissal finishes, return two variable .done(FastisValue?) and .cancel + The block to execute after the dismissal finishes, return two variable `.done(FastisValue?)` and `.cancel` + + Default value — `"nil"` */ public var dismissHandler: ((DismissAction) -> Void)? @@ -228,11 +235,15 @@ open class FastisController: UIViewController, JTACMonthView /** And initial value which will be selected by default + + Default value — `"nil"` */ public var initialValue: Value? /** Minimal selection date. Dates less then current will be marked as unavailable + + Default value — `"nil"` */ public var minimumDate: Date? { get { @@ -243,16 +254,10 @@ open class FastisController: UIViewController, JTACMonthView } } - /** - Allow date range changes - - Set this variable to `false` if you want to disable date range changes. - Next tap after selecting range will start new range selection. - */ - public var allowDateRangeChanges = true - /** Maximum selection date. Dates greater then current will be marked as unavailable + + Default value — `"nil"` */ public var maximumDate: Date? { get { @@ -499,7 +504,7 @@ open class FastisController: UIViewController, JTACMonthView return .from(date.startOfDay(in: self.config.calendar), to: date.endOfDay(in: self.config.calendar)) } - let dateRangeChangesDisabled = !self.allowDateRangeChanges + let dateRangeChangesDisabled = !self.privateAllowDateRangeChanges let rangeSelected = !oldValue.fromDate.isInSameDay(in: self.config.calendar, date: oldValue.toDate) if dateRangeChangesDisabled, rangeSelected { return .from(date.startOfDay(in: self.config.calendar), to: date.endOfDay(in: self.config.calendar)) @@ -702,6 +707,8 @@ public extension FastisController where Value == FastisRange { /** Set this variable to `true` if you want to allow select date ranges by tapping on months + + Default value — `"false"` */ var selectMonthOnHeaderTap: Bool { get { @@ -711,6 +718,24 @@ public extension FastisController where Value == FastisRange { self.privateSelectMonthOnHeaderTap = newValue } } + + /** + Allow date range changes + + Set this variable to `false` if you want to disable date range changes. + Next tap after selecting range will start new range selection. + + Default value — `"true"` + */ + var allowDateRangeChanges: Bool { + get { + self.privateAllowDateRangeChanges + } + set { + self.privateAllowDateRangeChanges = newValue + } + } + } public extension FastisController where Value == Date { @@ -726,7 +751,7 @@ public extension FastisController where Value == Date { /** Set this variable to `true` if you want to hide view of the selected date and close the controller right after the date is selected. - Default value — `"False"` + Default value — `"false"` */ var closeOnSelectionImmediately: Bool { get { diff --git a/Sources/Views/FastisView.swift b/Sources/Views/FastisView.swift index 532139c..b83e372 100644 --- a/Sources/Views/FastisView.swift +++ b/Sources/Views/FastisView.swift @@ -9,81 +9,117 @@ import SwiftUI /** -View of Fastis framework. Use it to create and present dade picker - - Usage example: - ```swiftUI - FastisView(mode: .range) { action in - switch action { - case .done(let newValue): - ... - case .cancel: - ... - } - } - .title = "Choose range" - .maximumDate = Date() - .allowToChooseNilDate = true - .shortcuts = [.today, .lastWeek] - ``` - - **Single and range modes** - - If you want to get a single date you have to use `Date` type: - - ```swiftUI -FastisView(mode: .single) { action in - switch action { - case .done(let resultDate): - print(resultDate) // resultDate is Date - case .cancel: - ... - } -} -.initialValue = Date() -.closeOnSelectionImmediately = true - ``` - - If you want to get a date range you have to use `FastisRange` type: - - ```swiftUI -FastisView(mode: .range) { action in - switch action { - case .done(let resultRange): - print(resultRange) // resultRange is FastisRange - case .cancel: - ... + View of Fastis framework. Use it to create and present dade picker + + Usage example: + ```swift + FastisView(mode: .range, dismissHandler: { action in + switch action { + case .done(let newValue): + ... + case .cancel: + ... + } + }) + .title("Choose range") + .allowToChooseNilDate(true) + .shortcuts([.lastWeek, .lastMonth]) + ``` + + **Single and range modes** + + If you want to get a single date you have to use `Date` type: + + ```swift + FastisView(mode: .single, dismissHandler: { action in + switch action { + case .done(let resultDate): + print(resultDate) // resultDate is Date + case .cancel: + ... + } + }) + .initialValue(Date()) + .closeOnSelectionImmediately(true) + ``` + + If you want to get a date range you have to use `FastisRange` type: + + ```swift + FastisView(mode: .range, dismissHandler: { action in + switch action { + case .done(let resultRange): + print(resultRange) // resultRange is FastisRange + case .cancel: + ... + } + }) + .initialValue(FastisRange(from: Date(), to: Date())) // or .from(Date(), to: Date()) + ``` + */ +public struct FastisView: UIViewControllerRepresentable { + + private let controller: FastisController + + /// Initiate FastisView + /// - Parameter config: Configuration parameters + /// - Parameter dismissHandler: The block to execute after the dismissal finishes, return two variable .done(FastisValue?) and .cancel + public init( + config: FastisConfig = .default, + dismissHandler: ((FastisController.DismissAction) -> Void)? = nil + ) { + self.controller = FastisController(config: config) + self.controller.dismissHandler = dismissHandler } - } -.initialValue = FastisRange(from: Date(), to: Date()) // or .from(Date(), to: Date()) - ``` - */ -public struct FastisView: UIViewControllerRepresentable { + public func makeUIViewController(context: Context) -> UINavigationController { + UINavigationController(rootViewController: self.controller) + } - private var privateSelectMonthOnHeaderTap = false - private var privateCloseOnSelectionImmediately = false - private let config: FastisConfig + public func updateUIViewController( + _ uiViewController: UINavigationController, + context: UIViewControllerRepresentableContext + ) { } /** - And title controller + Title of view + + Default value — `"nil"` */ - public var title: String? = nil + public func title(_ value: String?) -> Self { + self.controller.title = value + return self + } /** And initial value which will be selected by default + + Default value — `"nil"` */ - public var initialValue: Value? = nil + public func initialValue(_ value: Value?) -> Self { + self.controller.initialValue = value + return self + } /** Minimal selection date. Dates less then current will be marked as unavailable + + Default value — `"nil"` */ - public var minimumDate: Date? = nil - + public func minimumDate(_ value: Date?) -> Self { + self.controller.minimumDate = value + return self + } + /** Maximum selection date. Dates greater then current will be marked as unavailable + + Default value — `"nil"` */ - public var maximumDate: Date? = nil + public func maximumDate(_ value: Date?) -> Self { + self.controller.maximumDate = value + return self + } /** Allow to choose `nil` date @@ -91,20 +127,19 @@ public struct FastisView: UIViewControllerRepresentable { When `allowToChooseNilDate` is `true`: * "Done" button will be always enabled * You will be able to reset selection by you tapping on selected date again - */ - public var allowToChooseNilDate: Bool = false - - /** - Allow date range changes - Set this variable to `false` if you want to disable date range changes. - Next tap after selecting range will start new range selection. + Default value — `"false"` */ - public var allowDateRangeChanges: Bool = true + public func allowToChooseNilDate(_ value: Bool) -> Self { + self.controller.allowToChooseNilDate = value + return self + } /** Shortcuts array + Default value — `"[]"` + You can use prepared shortcuts depending on the current mode. - For `.single` mode: `.today`, `.tomorrow`, `.yesterday` @@ -119,48 +154,11 @@ public struct FastisView: UIViewControllerRepresentable { } ``` */ - public var shortcuts: [FastisShortcut] = [] - - /** - The block to execute after the dismissal finishes, return two variable .done(FastisValue?) and .cancel - */ - public var dismissHandler: ((FastisController.DismissAction) -> Void)? = nil - - /// Initiate FastisView - /// - Parameter config: Configuration parameters - public init( - config: FastisConfig = .default, - dismissHandler: ((FastisController.DismissAction) -> Void)? = nil - ) { - self.config = config - self.dismissHandler = dismissHandler - } - - public func makeUIViewController(context: Context) -> UINavigationController { - let fastisController = FastisController(config: self.config) - fastisController.title = self.title - fastisController.initialValue = self.initialValue - fastisController.minimumDate = self.minimumDate - fastisController.maximumDate = self.maximumDate - fastisController.allowToChooseNilDate = self.allowToChooseNilDate - fastisController.allowDateRangeChanges = self.allowDateRangeChanges - fastisController.shortcuts = self.shortcuts - fastisController.dismissHandler = self.dismissHandler - switch Value.mode { - case .single: - let singleController = fastisController as? FastisController - singleController?.closeOnSelectionImmediately = self.privateCloseOnSelectionImmediately - case .range: - let rangeController = fastisController as? FastisController - rangeController?.selectMonthOnHeaderTap = self.privateSelectMonthOnHeaderTap - } - return UINavigationController(rootViewController: fastisController) + public func shortcuts(_ value: [FastisShortcut]) -> Self { + self.controller.shortcuts = value + return self } - public func updateUIViewController( - _ uiViewController: UINavigationController, - context: UIViewControllerRepresentableContext - ) {} } public extension FastisView where Value == FastisRange { @@ -177,25 +175,38 @@ public extension FastisView where Value == FastisRange { self.init(config: config, dismissHandler: dismissHandler) } + /** + Allow date range changes + + Set this variable to `false` if you want to disable date range changes. + Next tap after selecting range will start new range selection. + + Default value — `"true"` + */ + func allowDateRangeChanges(_ value: Bool) -> Self { + self.controller.allowDateRangeChanges = value + return self + } + /** Set this variable to `true` if you want to allow select date ranges by tapping on months + + Default value — `"false"` */ - var selectMonthOnHeaderTap: Bool { - get { - self.privateSelectMonthOnHeaderTap - } - set { - self.privateSelectMonthOnHeaderTap = newValue - } + func selectMonthOnHeaderTap(_ value: Bool) -> Self { + self.controller.selectMonthOnHeaderTap = value + return self } + } public extension FastisView where Value == Date { /// Initiate FastisView /// - Parameters: - /// - mode: Choose .range or .single mode + /// - mode: Choose `.range` or `.single` mode /// - config: Custom configuration parameters. Default value is equal to `FastisConfig.default` + /// - dismissHandler: The block to execute after the dismissal finishes, return two variable .done(FastisValue?) and .cancel init( mode: FastisModeSingle, config: FastisConfig = .default, @@ -207,86 +218,11 @@ public extension FastisView where Value == Date { /** Set this variable to `true` if you want to hide view of the selected date and close the controller right after the date is selected. - Default value — `"False"` + Default value — `"false"` */ - var closeOnSelectionImmediately: Bool { - get { - self.privateCloseOnSelectionImmediately - } - set { - self.privateCloseOnSelectionImmediately = newValue - } + func closeOnSelectionImmediately(_ value: Bool) -> Self { + self.controller.closeOnSelectionImmediately = value + return self } -} -public extension FastisView { - - /// Modifier for property 'title' - func title(_ value: String?) -> FastisView { - var view = self - view.title = value - return view - } - - /// Modifier for property 'initialValue' - func initialValue(_ value: Value?) -> FastisView { - var view = self - view.initialValue = value - return view - } - - /// Modifier for property 'minimumDate' - func minimumDate(_ value: Date?) -> FastisView { - var view = self - view.minimumDate = value - return view - } - - /// Modifier for property 'maximumDate' - func maximumDate(_ value: Date?) -> FastisView { - var view = self - view.maximumDate = value - return view - } - - /// Modifier for property 'allowToChooseNilDate' - func allowToChooseNilDate(_ value: Bool) -> FastisView { - var view = self - view.allowToChooseNilDate = value - return view - } - - /// Modifier for property 'allowDateRangeChanges' - func allowDateRangeChanges(_ value: Bool) -> FastisView { - var view = self - view.allowDateRangeChanges = value - return view - } - - /// Modifier for property 'shortcuts' - func shortcuts(_ value: [FastisShortcut]) -> FastisView { - var view = self - view.shortcuts = value - return view - } -} - -public extension FastisView where Value == FastisRange { - - /// Modifier for property 'selectMonthOnHeaderTap' - func selectMonthOnHeaderTap(_ value: Bool) -> FastisView { - var view = self - view.selectMonthOnHeaderTap = value - return view - } -} - -public extension FastisView where Value == Date { - - /// Modifier for property 'closeOnSelectionImmediately' - func closeOnSelectionImmediately(_ value: Bool) -> FastisView { - var view = self - view.closeOnSelectionImmediately = value - return view - } }