Skip to content

Commit

Permalink
fix #48 FastisView for presentation in SwiftUI
Browse files Browse the repository at this point in the history
  • Loading branch information
UriyDevyataev committed Mar 14, 2024
1 parent debd688 commit 2c1723c
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 6 deletions.
10 changes: 9 additions & 1 deletion Example/FastisExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
1AC064EF2BA2E6DB003AFCCE /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC064EE2BA2E6DB003AFCCE /* MainView.swift */; };
1AC064F32BA2E708003AFCCE /* HostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC064F22BA2E708003AFCCE /* HostingController.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 */; };
Expand All @@ -19,6 +21,8 @@
1A3E7A5F2A40901600434229 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
1A3E7A612A40902000434229 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
1A3E7A622A409B3A00434229 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
1AC064EE2BA2E6DB003AFCCE /* MainView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
1AC064F22BA2E708003AFCCE /* HostingController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HostingController.swift; sourceTree = "<group>"; };
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 = "<group>"; };
F3FCEE43244780FE000F966E /* FastisExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FastisExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -79,6 +83,8 @@
isa = PBXGroup;
children = (
F3FCEE46244780FE000F966E /* AppDelegate.swift */,
1AC064EE2BA2E6DB003AFCCE /* MainView.swift */,
1AC064F22BA2E708003AFCCE /* HostingController.swift */,
F3FCEE4A244780FE000F966E /* ViewController.swift */,
F3FCEE4F24478100000F966E /* Assets.xcassets */,
F3FCEE5124478100000F966E /* LaunchScreen.storyboard */,
Expand Down Expand Up @@ -164,8 +170,10 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1AC064F32BA2E708003AFCCE /* HostingController.swift in Sources */,
F3FCEE4B244780FE000F966E /* ViewController.swift in Sources */,
F3FCEE47244780FE000F966E /* AppDelegate.swift in Sources */,
1AC064EF2BA2E6DB003AFCCE /* MainView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
24 changes: 24 additions & 0 deletions Example/Source/HostingController.swift
Original file line number Diff line number Diff line change
@@ -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<MainView> {

init() {
super.init(rootView: MainView())
}

@MainActor required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
110 changes: 110 additions & 0 deletions Example/Source/MainView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//
// 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()
}
})
})
.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()
}

}
26 changes: 21 additions & 5 deletions Example/Source/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}()

Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
Expand All @@ -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)
}

}
160 changes: 160 additions & 0 deletions Sources/Views/FastisView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
//
// 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<Value: FastisValue>: 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<Value>] = []
public var dismissHandler: ((FastisController<Value>.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<Value>(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<Date>
singleController?.closeOnSelectionImmediately = self.privateCloseOnSelectionImmediately
case .range:
let rangeController = fastisController as? FastisController<FastisRange>
rangeController?.selectMonthOnHeaderTap = self.privateSelectMonthOnHeaderTap
}
return UINavigationController(rootViewController: fastisController)
}

public func updateUIViewController(
_ uiViewController: UINavigationController,
context: UIViewControllerRepresentableContext<FastisView>
) {}
}

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
}
}
}

0 comments on commit 2c1723c

Please sign in to comment.