├── Example
├── Example
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── Colors
│ │ │ ├── Contents.json
│ │ │ ├── black.colorset
│ │ │ │ └── Contents.json
│ │ │ └── yellow.colorset
│ │ │ │ └── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── ExampleApp.swift
│ ├── Extension
│ │ ├── Color+Extension.swift
│ │ └── View+Extension.swift
│ ├── Common
│ │ ├── ThemeCard.swift
│ │ ├── Utils.swift
│ │ └── LocalizedString.swift
│ ├── ViewModel
│ │ └── PickerViewModel.swift
│ └── View
│ │ └── PickerExampleView.swift
├── en.lproj
│ └── Localizable.strings
└── Example.xcodeproj
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcuserdata
│ │ └── rizwana.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ ├── xcuserdata
│ └── rizwana.xcuserdatad
│ │ ├── xcschemes
│ │ └── xcschememanagement.plist
│ │ └── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ ├── xcshareddata
│ └── xcschemes
│ │ └── Example.xcscheme
│ └── project.pbxproj
├── Sources
└── SSDateTimePicker
│ ├── SSDateTimePicker
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── darkGreen.colorset
│ │ │ └── Contents.json
│ │ ├── darkPink.colorset
│ │ │ └── Contents.json
│ │ ├── peach.colorset
│ │ │ └── Contents.json
│ │ ├── lightBlue.colorset
│ │ │ └── Contents.json
│ │ ├── lightGreen.colorset
│ │ │ └── Contents.json
│ │ └── lightPink.colorset
│ │ │ └── Contents.json
│ ├── en.lproj
│ │ └── Localizable.strings
│ ├── Common
│ │ ├── SSImageConstant.swift
│ │ ├── SSLocalizedString.swift
│ │ ├── Time picker
│ │ │ ├── SSCustomWheelPicker.swift
│ │ │ ├── SSTimePickerManager.swift
│ │ │ └── SSTimePickerConfiguration.swift
│ │ ├── SSCornerRadiusStyle.swift
│ │ ├── SSUtils.swift
│ │ ├── SSPickerConstants.swift
│ │ └── Date picker
│ │ │ ├── SSDatePickerConfiguration.swift
│ │ │ └── SSDatePickerManager.swift
│ ├── DateTimePicker.docc
│ │ └── DateTimePicker.md
│ ├── Views
│ │ ├── SSThemeButton.swift
│ │ ├── Date picker
│ │ │ ├── SSWeekDatesView.swift
│ │ │ ├── SSMultiDatePicker.swift
│ │ │ ├── SSDateRangePicker.swift
│ │ │ ├── SSMonthSelectionView.swift
│ │ │ ├── SSYearSelectionView.swift
│ │ │ ├── SSDateView.swift
│ │ │ └── SSDatePicker.swift
│ │ └── Time Picker
│ │ │ ├── SSTimeTextField.swift
│ │ │ ├── SSClockPicker.swift
│ │ │ └── SSTimePicker.swift
│ ├── DateTimePicker.h
│ └── Extensions
│ │ ├── SSInt+Extension.swift
│ │ ├── SSDateComponents+Extension.swift
│ │ ├── SSColor+Extension.swift
│ │ ├── SSString+Extension.swift
│ │ ├── SSDateFormatter+Extension.swift
│ │ ├── SSView+Extension.swift
│ │ ├── SSDate+Extension.swift
│ │ └── SSCalendar+Extension.swift
│ ├── SSDateTimePicker.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── project.pbxproj
│ └── SSDateTimePicker.podspec
├── .gitignore
├── Package.swift
├── SSDateTimePicker.podspec
├── LICENSE
└── README.md
/Example/Example/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Example/Assets.xcassets/Colors/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Example/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | xcuserdata/
5 | DerivedData/
6 | .swiftpm/configuration/registries.json
7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8 | .netrc
9 |
--------------------------------------------------------------------------------
/Example/en.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Example
4 |
5 | Created by Rizwana Desai on 12/12/23.
6 |
7 | */
8 |
9 | "Ok" = "Ok";
10 | "cancel" = "Cancel";
11 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/project.xcworkspace/xcuserdata/rizwana.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SSDateTimePicker/HEAD/Example/Example.xcodeproj/project.xcworkspace/xcuserdata/rizwana.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/en.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | DateTimePicker
4 |
5 | Created by Rizwana Desai on 12/12/23.
6 |
7 | */
8 |
9 |
10 | "Ok" = "Ok";
11 | "Cancel" = "Cancel";
12 | "Select Date" = "Select Date";
13 | "Select Time" = "Select Time";
14 | "AM" = "AM";
15 | "PM" = "PM";
16 |
--------------------------------------------------------------------------------
/Example/Example/ExampleApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExampleApp.swift
3 | // Example
4 | //
5 | // Created by Rizwana Desai on 22/11/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct ExampleApp: App {
12 |
13 | var body: some Scene {
14 | WindowGroup {
15 | PickerExampleView()
16 | }
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/Example/Extension/Color+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color+Extension.swift
3 | // Example
4 | //
5 | // Created by Rizwana Desai on 12/12/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | extension Color {
12 |
13 | public static let themeBlack = Color("black")
14 | public static let themeYellow = Color("yellow")
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Common/SSImageConstant.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageConstant.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 23/11/23.
6 | //
7 |
8 | import Foundation
9 |
10 | struct SSImageConstant {
11 |
12 | static let chevronRight = "chevron.right"
13 | static let chevronLeft = "chevron.left"
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/DateTimePicker.docc/DateTimePicker.md:
--------------------------------------------------------------------------------
1 | # ``DateTimePicker``
2 |
3 | Summary
4 |
5 | ## Overview
6 |
7 | Text
8 |
9 | ## Topics
10 |
11 | ### Group
12 |
13 | - ``Symbol``
--------------------------------------------------------------------------------
/Example/Example/Extension/View+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // View+Extension.swift
3 | // Example
4 | //
5 | // Created by Rizwana Desai on 15/12/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | extension View {
11 |
12 | func themeButton() -> some View {
13 | self.modifier(ThemeButton())
14 | }
15 |
16 | func themeCard() -> some View {
17 | self.modifier(ThemeCard())
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/Example/Example/Assets.xcassets/Colors/black.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "18",
9 | "green" : "18",
10 | "red" : "18"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Example/Example/Assets.xcassets/Colors/yellow.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "134",
9 | "green" : "204",
10 | "red" : "247"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Assets.xcassets/darkGreen.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "43",
9 | "green" : "105",
10 | "red" : "68"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Assets.xcassets/darkPink.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "105",
9 | "green" : "94",
10 | "red" : "221"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Assets.xcassets/peach.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "213",
9 | "green" : "209",
10 | "red" : "244"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Assets.xcassets/lightBlue.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "252",
9 | "green" : "244",
10 | "red" : "242"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Assets.xcassets/lightGreen.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "221",
9 | "green" : "234",
10 | "red" : "230"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Assets.xcassets/lightPink.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "250",
9 | "green" : "250",
10 | "red" : "253"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Views/SSThemeButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ThemeButton.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 01/12/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SSThemeButtonModifier: ViewModifier {
11 |
12 | var textColor: Color
13 | var font: Font
14 |
15 | func body(content: Content) -> some View {
16 | content
17 | .font(font)
18 | .padding(SSPickerConstants.paddingTen)
19 | .foregroundColor(textColor)
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/Example/Example/Common/ThemeCard.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ThemeCard.swift
3 | // Example
4 | //
5 | // Created by Rizwana Desai on 15/12/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ThemeCard: ViewModifier {
11 |
12 | func body(content: Content) -> some View {
13 | content
14 | .padding(20)
15 | .background(Color.lightPink)
16 | .clipShape(RoundedRectangle(cornerRadius: 0.0))
17 | .shadow(color: .peach, radius: 2)
18 | .padding(EdgeInsets(top: 10, leading: 20, bottom: 0, trailing: 20))
19 | }
20 |
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/DateTimePicker.h:
--------------------------------------------------------------------------------
1 | //
2 | // DateTimePicker.h
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 22/11/23.
6 | //
7 |
8 | #import
9 |
10 | //! Project version number for DateTimePicker.
11 | FOUNDATION_EXPORT double DateTimePickerVersionNumber;
12 |
13 | //! Project version string for DateTimePicker.
14 | FOUNDATION_EXPORT const unsigned char DateTimePickerVersionString[];
15 |
16 | // In this header, you should import all the public headers of your framework using statements like #import
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/xcuserdata/rizwana.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Example.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 65E058492B0E3E850049A7BA
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Common/SSLocalizedString.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LocalizedString.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 12/12/23.
6 | //
7 |
8 | import Foundation
9 |
10 | class SSLocalizedString {
11 |
12 | static let ok = "Ok".localized()
13 | static let cancel = "Cancel".localized()
14 | static let selectDate = "Select Date".localized()
15 | static let selectTime = "Select Time".localized()
16 | static let am = "AM".localized()
17 | static let pm = "PM".localized()
18 | static let hour = "HH".localized()
19 | static let minute = "MM".localized()
20 | static let done = "Done".localized()
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Extensions/SSInt+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Int+Extension.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 06/12/23.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Int {
11 |
12 | // MARK: - Formatted Time String
13 |
14 | /// Converts an integer representing time (e.g., hours, minutes, seconds) into a two-digit formatted string.
15 | ///
16 | /// This extension is useful for ensuring a consistent two-digit representation, adding leading zeros if necessary.
17 | ///
18 | /// Example:
19 | /// ```
20 | /// let hour = 8
21 | /// let formattedHour = hour.formattedTime // "08"
22 | /// ```
23 | var formattedTime: String {
24 | String(format: "%02d", self)
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Common/Time picker/SSCustomWheelPicker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SSCustomWheelPicker.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 04/12/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SSCustomWheelPicker: View {
11 |
12 | var body: some View {
13 | wheelPicker
14 | .frame(height: 200)
15 | }
16 |
17 | var wheelPicker: some View {
18 | ScrollView() {
19 | VStack {
20 | ForEach(0..<60, id: \.self) { time in
21 | Text("\(time)")
22 | }
23 | }
24 | }
25 | .frame(height: 100)
26 | }
27 |
28 | }
29 |
30 | struct SSCustomWheelPicker_Previews: PreviewProvider {
31 | static var previews: some View {
32 | SSCustomWheelPicker()
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker.podspec:
--------------------------------------------------------------------------------
1 |
2 | Pod::Spec.new do |spec|
3 |
4 | spec.name = "SSDateTimePicker"
5 | spec.version = "1.0.0"
6 | spec.summary = "Date and time selection picker."
7 | spec.description = "SSDateTimePicker offers versatile date and time selection options, including single date, multiple date, and date range selection."
8 | spec.homepage = "https://github.com/SimformSolutionsPvtLtd/SSDateTimePicker"
9 | spec.license = "MIT"
10 |
11 | spec.author = { "Rizwana" => "rizwana@simformsolutions.com" }
12 |
13 | spec.platform = :ios, "15.0"
14 | spec.source = { :git => "https://github.com/SimformSolutionsPvtLtd/SSDateTimePicker.git", :tag => spec.version.to_s }
15 | spec.source_files = "SSDateTimePicker/**/*.{swift}"
16 | spec.swift_versions = "5.9"
17 |
18 | end
19 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.9
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "SSDateTimePicker",
8 | defaultLocalization: "en", platforms: [.iOS(.v15)],
9 |
10 | products: [
11 | // Products define the executables and libraries a package produces, making them visible to other packages.
12 | .library(
13 | name: "SSDateTimePicker",
14 | targets: ["SSDateTimePicker"]),
15 | ],
16 | targets: [
17 | // Targets are the basic building blocks of a package, defining a module or a test suite.
18 | // Targets can depend on other targets in this package and products from dependencies.
19 | .target(
20 | name: "SSDateTimePicker"),
21 |
22 | ]
23 | )
24 |
--------------------------------------------------------------------------------
/Example/Example/ViewModel/PickerViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PickerViewModel.swift
3 | // Example
4 | //
5 | // Created by Rizwana Desai on 01/12/23.
6 | //
7 |
8 | import Foundation
9 | import SSDateTimePicker
10 | import SwiftUI
11 |
12 | final class PickerViewModel: ObservableObject {
13 |
14 | // MARK: - Properties
15 |
16 | @Published var selectedDate: Date?
17 | @Published var selectedDates: [Date]?
18 | @Published var selectedDateRange: DateRange?
19 | @Published var selectedTime: Date?
20 |
21 | //MARK: - Initializer
22 |
23 | init() {
24 |
25 | }
26 |
27 | //MARK: - Methods
28 |
29 | func getSelectedDates() -> String? {
30 | let dates = self.selectedDates?.compactMap({ date in
31 | date.monthDateYear
32 | }).joined(separator: ",")
33 | return dates
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Extensions/SSDateComponents+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DateComponents+Extension.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 24/11/23.
6 | //
7 |
8 | import Foundation
9 |
10 | extension DateComponents {
11 |
12 | // MARK: - Every Day Date Components
13 |
14 | /// Represents a `DateComponents` instance with zeroed hours, minutes, and seconds,
15 | /// effectively representing the concept of "every day."
16 | ///
17 | /// Example:
18 | /// ```swift
19 | /// let dailyResetTime = DateComponents.everyDay
20 | /// let nextResetDate = Calendar.current.date(byAdding: dailyResetTime, to: Date())
21 | /// ```
22 | ///
23 | /// - Returns: A `DateComponents` instance representing midnight, the start of every day.
24 | static var everyDay: DateComponents {
25 | DateComponents(hour: 0, minute: 0, second: 0)
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Extensions/SSColor+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color+Extension.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 24/11/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | public extension Color {
12 |
13 | static let lightGreen: Color = Color(red: 230, green: 234, blue: 221)
14 | static let darkGreen: Color = Color(red: 68, green: 105, blue: 43)
15 | static let darkPink: Color = Color(red: 221, green: 94, blue: 105)
16 | static let lightPink: Color = Color(red: 253, green: 250, blue: 250)
17 | static let lightBlue: Color = Color(red: 242, green: 244, blue: 252)
18 | static let peach: Color = Color(red: 244, green: 209, blue: 213)
19 |
20 | }
21 |
22 | fileprivate extension Color {
23 |
24 | init(red: Int, green: Int, blue: Int) {
25 | self.init(red: Double(red)/255, green: Double(green)/255, blue: Double(blue)/255)
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/SSDateTimePicker.podspec:
--------------------------------------------------------------------------------
1 |
2 | Pod::Spec.new do |spec|
3 |
4 | spec.name = "SSDateTimePicker"
5 | spec.version = "1.0.0"
6 | spec.summary = "Date and time selection picker."
7 | spec.description = "SSDateTimePicker offers versatile date and time selection options, including single date, multiple date, and date range selection."
8 | spec.homepage = "https://github.com/SimformSolutionsPvtLtd/SSDateTimePicker.git"
9 | spec.license = { :type => 'MIT', :file => 'LICENSE' }
10 |
11 | spec.author = { "Rizwana" => "rizwana@simformsolutions.com" }
12 |
13 | spec.platform = :ios, "15.0"
14 | spec.source = { :git => "https://github.com/SimformSolutionsPvtLtd/SSDateTimePicker.git", :tag => spec.version.to_s }
15 | spec.source_files = "Sources", "Sources/SSDateTimePicker/SSDateTimePicker/**/*.{swift, xcassets}"
16 | spec.resources = "Sources/SSDateTimePicker/**/*.{xcassets,storyboard,xib,plist,json,strings,lproj}"
17 | spec.swift_versions = "5.9"
18 |
19 | end
20 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Extensions/SSString+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+Extension.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 12/12/23.
6 | //
7 |
8 | import Foundation
9 |
10 | extension String {
11 |
12 | // MARK: - Localized String with Comment
13 |
14 | /// Returns a localized version of the string, using the NSLocalizedString function, with an optional comment.
15 | ///
16 | /// - Parameters:
17 | /// - comment: An optional comment that can be used to provide context or additional information for translators.
18 | ///
19 | /// Example:
20 | /// ```
21 | /// let greeting = "Hello".localized(withComment: "User greeting")
22 | /// print(greeting) // Localized version of "Hello" with the specified comment.
23 | /// ```
24 | ///
25 | /// - Returns: A localized version of the string.
26 | func localized(withComment comment: String = "") -> String {
27 | return NSLocalizedString(self, comment: comment)
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/xcuserdata/rizwana.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Common/SSCornerRadiusStyle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CornerRadiusStyle.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 30/11/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | struct SSCornerRadiusStyle: ViewModifier {
12 |
13 | // MARK: - Properties
14 |
15 | var radius: CGFloat
16 | var corners: UIRectCorner
17 |
18 | //MARK: - Body
19 |
20 | func body(content: Content) -> some View {
21 | content
22 | .clipShape(SSCornerRadiusShape(radius: radius, corners: corners))
23 | }
24 |
25 | }
26 |
27 | struct SSCornerRadiusShape: Shape {
28 |
29 | //MARK: - Property
30 |
31 | var radius = CGFloat.infinity
32 | var corners = UIRectCorner.allCorners
33 |
34 | //MARK: - Methods
35 |
36 | func path(in rect: CGRect) -> Path {
37 | let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
38 | return Path(path.cgPath)
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/Example/Example/Common/Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ThemeButton.swift
3 | // Example
4 | //
5 | // Created by Rizwana Desai on 12/12/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ThemeButton: ViewModifier {
11 |
12 | func body(content: Content) -> some View {
13 | content
14 | .frame(maxWidth: .infinity, minHeight: 44)
15 | .font(.system(size: 14, weight: .semibold))
16 | .background(Color.darkPink)
17 | .foregroundColor(Color.white)
18 | .cornerRadius(10)
19 | .padding(.leading, 20)
20 | .padding(.trailing, 20)
21 | }
22 |
23 | }
24 |
25 | struct ClearBackgroundView: UIViewRepresentable {
26 |
27 | func makeUIView(context: Context) -> UIView {
28 | return InnerView()
29 | }
30 |
31 | func updateUIView(_ uiView: UIView, context: Context) {
32 | }
33 |
34 | private class InnerView: UIView {
35 | override func didMoveToWindow() {
36 | super.didMoveToWindow()
37 | superview?.superview?.backgroundColor = .clear
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/Example/Example/Common/LocalizedString.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LocalizedString.swift
3 | // Example
4 | //
5 | // Created by Rizwana Desai on 12/12/23.
6 | //
7 |
8 | import Foundation
9 |
10 | class LocalizedString {
11 |
12 | static let multipleDateSelectionExample = "Multiple date selection Example."
13 | static let singleDateSelectionExample = "Single date selection Example."
14 | static let dateRangeSelectionExample = "Date range selection Example."
15 | static let timePickerExample = "Time Picker Example"
16 | static let selectMultipleDate = "Select Multiple Date"
17 | static let selectSingleDate = "Select Single Date"
18 | static let selectDateRange = "Select Date Range"
19 | static let selectTime = "Select Time"
20 | static let customizedDatePicker = "Customized Date Picker Example"
21 | static let startDate = "Start Date:"
22 | static let endDate = "End Date:"
23 | static let selectedDate = "Selected Date:"
24 | static let selectedDates = "Selected Dates:"
25 | static let selectedTime = "Selected Time:"
26 | static let dateTimePickerExample = "SSDateTimePicker Example"
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Simform Solutions
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Views/Date picker/SSWeekDatesView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DatesView.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 23/11/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SSWeekDatesView: View, DatePickerConfigurationDirectAccess {
11 |
12 | // MARK: - Properties
13 |
14 | @EnvironmentObject var calendarManager: SSDatePickerManager
15 | var week: Date
16 |
17 | var configuration: SSDatePickerConfiguration {
18 | calendarManager.configuration
19 | }
20 |
21 | private var dates: [Date] {
22 | guard let weekInterval = calendar.dateInterval(of: .weekOfYear, for: week) else {
23 | return []
24 | }
25 | return calendar.generateDates(
26 | inside: weekInterval,
27 | matching: .everyDay)
28 | }
29 |
30 | //MARK: - Initializer
31 | init(week: Date) {
32 | self.week = week
33 | }
34 |
35 | //MARK: - Body
36 |
37 | var body: some View {
38 | datesForWeek
39 | }
40 |
41 | //MARK: - Sub views
42 |
43 | private var datesForWeek: some View {
44 | HStack(spacing: SSPickerConstants.horizontalSpacingDates) {
45 | ForEach(dates, id: \.self) { date in
46 | SSDateView(date: date, weekDateInSelectedMonth: week)
47 | }
48 | }
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Extensions/SSDateFormatter+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DateFormatter+Extension.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 24/11/23.
6 | //
7 |
8 | import Foundation
9 |
10 | extension DateFormatter {
11 |
12 | // MARK: - Months List
13 |
14 | /// Provides an array of localized month names using the current locale.
15 | ///
16 | /// Example:
17 | /// ```
18 | /// let months = DateFormatter.monthsList
19 | /// print(months) // ["January", "February", ..., "December"]
20 | /// ```
21 | static var monthsList: [String] {
22 | let formatter = DateFormatter()
23 | let months = formatter.monthSymbols
24 | return months ?? []
25 | }
26 |
27 | // MARK: - Date Formatter with Format String
28 |
29 | /// Returns a date formatter instance configured with the specified format string.
30 | ///
31 | /// - Parameter formate: The format string for the desired date format.
32 | ///
33 | /// Example:
34 | /// ```swift
35 | /// let customDateFormatter = DateFormatter.configure(with: "yyyy-MM-dd HH:mm:ss")
36 | /// let formattedDate = customDateFormatter.string(from: Date())
37 | /// print(formattedDate) // Formatted date string using the specified format.
38 | /// ```
39 | ///
40 | /// - Returns: A `DateFormatter` instance with the specified date format.
41 | static func configure(with formate: String) -> DateFormatter {
42 | let formatter = DateFormatter()
43 | formatter.dateFormat = formate
44 | return formatter
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Views/Date picker/SSMultiDatePicker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SSMultiDatePicker.swift
3 | // SSDateTimePicker
4 | //
5 | // Created by Rizwana Desai on 18/12/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public struct SSMultiDatePicker: View {
11 |
12 | // MARK: - Properties
13 |
14 | var datePicker: SSDatePicker
15 |
16 | //MARK: - Initializer
17 |
18 | init(_ datePicker: SSDatePicker) {
19 | self.datePicker = datePicker
20 | }
21 |
22 | //MARK: - Body
23 |
24 | public var body: some View {
25 | datePicker
26 | }
27 |
28 | }
29 |
30 | // MARK: - Modifiers
31 |
32 | extension SSMultiDatePicker {
33 |
34 | /// Sets a callback closure to be executed when multiple dates are selected.
35 | ///
36 | /// - Parameter completion: A closure to be called when multiple dates are selected. The closure takes an array of `Date` as a parameter.
37 | /// - Returns: The modified `SSMultiDatePicker` instance.
38 | public func onMultiDateSelection(_ completion: @escaping ([Date]) -> ()) -> SSMultiDatePicker {
39 | let picker = self
40 | picker.datePicker.datePickerManager.multiDateSelectionCallback = completion
41 | return picker
42 | }
43 |
44 | /// Sets the selected dates for the multi-date picker.
45 | ///
46 | /// - Parameter dates: An optional array of `Date` representing the selected dates. Pass `nil` to clear the selection.
47 | /// - Returns: The modified `SSMultiDatePicker` instance.
48 | public func selectedDates(_ dates: [Date]?) -> SSMultiDatePicker {
49 | let picker = self
50 | picker.datePicker.datePickerManager.selectedDates = dates
51 | return picker
52 | }
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Views/Date picker/SSDateRangePicker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SSDateRangePicker.swift
3 | // SSDateTimePicker
4 | //
5 | // Created by Rizwana Desai on 18/12/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public struct SSDateRangePicker: View {
11 |
12 | // MARK: - Properties
13 |
14 | var datePicker: SSDatePicker
15 |
16 | // MARK: - Initializer
17 |
18 | init(_ datePicker: SSDatePicker) {
19 | self.datePicker = datePicker
20 | }
21 |
22 | //MARK: - Body
23 |
24 | public var body: some View {
25 | datePicker
26 | }
27 |
28 | }
29 |
30 | // MARK: - Modifiers
31 |
32 | extension SSDateRangePicker {
33 |
34 | /// Sets a callback closure to be executed when a date range is selected.
35 | ///
36 | /// - Parameter completion: A closure to be called when a date range is selected. The closure takes a `DateRange` parameter.
37 | /// - Returns: The modified `SSDateRangePicker` instance.
38 | public func onDateRangeSelection(_ completion: @escaping (DateRange) -> ()) -> SSDateRangePicker {
39 | let picker = self
40 | picker.datePicker.datePickerManager.dateRangeSelectionCallback = completion
41 | return picker
42 | }
43 |
44 | /// Sets the selected dates for the date range picker.
45 | ///
46 | /// - Parameter dateRange: An optional `DateRange` representing the selected date range. Pass `nil` to clear the selection.
47 | /// - Returns: The modified `SSDateRangePicker` instance.
48 | public func selectedDateRange(_ dateRange: DateRange?) -> SSDateRangePicker {
49 | let picker = self
50 | picker.datePicker.datePickerManager.startDate = dateRange?.startDate
51 | picker.datePicker.datePickerManager.endDate = dateRange?.endDate
52 | return picker
53 | }
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Views/Date picker/SSMonthSelectionView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MonthSelectionView.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 24/11/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SSMonthSelectionView: View, DatePickerConfigurationDirectAccess {
11 |
12 | // MARK: - Properties
13 |
14 | @EnvironmentObject var calendarManager: SSDatePickerManager
15 | @State var monthList: [String] = DateFormatter.monthsList
16 | private var gridItem: [GridItem] = Array(repeating: .init(.flexible()), count: SSPickerConstants.monthYearGridRows)
17 |
18 | var configuration: SSDatePickerConfiguration {
19 | calendarManager.configuration
20 | }
21 |
22 | //MARK: - Body
23 |
24 | var body: some View {
25 | monthsGridView
26 | .padding(.top, SSPickerConstants.monthYearViewTopSpace)
27 | .padding(.bottom, SSPickerConstants.monthYearViewBottomSpace)
28 | }
29 |
30 | //MARK: - Sub views
31 |
32 | private var monthsGridView: some View {
33 | HStack {
34 | LazyVGrid(columns: gridItem, spacing: SSPickerConstants.monthYearGridSpacing) {
35 | ForEach(monthList, id: \.self) { month in
36 | btnMonth(for: month)
37 | }
38 | }
39 | }
40 | }
41 |
42 | @ViewBuilder
43 | private func btnMonth(for month: String) -> some View {
44 | let monthName = month
45 | let isSelectedMonth = calendarManager.isSelected(monthName)
46 | Button {
47 | updateMonth(month: month)
48 | } label: {
49 | Text(monthName)
50 | .font(isSelectedMonth ? selectedMonthTextFont : monthTextFont)
51 | .foregroundColor(isSelectedMonth ? selectionBackgroundColor : dateMonthYearTextColor)
52 | }
53 | }
54 |
55 | //MARK: - Methods
56 |
57 | private func updateMonth(month: String) {
58 | guard let month = monthList.firstIndex(where: { $0 == month}) else { return }
59 | calendarManager.updateMonthSelection(month: month+1)
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Extensions/SSView+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // View+Extension.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 30/11/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | extension View {
12 |
13 | /// Applies the given transform if the given condition evaluates to `true`.
14 | /// - Parameters:
15 | /// - condition: The condition to evaluate.
16 | /// - transform: The transform to apply to the source `View`.
17 | /// - Returns: Either the original `View` or the modified `View` if the condition is `true`.
18 | @ViewBuilder func `if`(_ condition: Bool, transform: (Self) -> Content) -> some View {
19 | if condition {
20 | transform(self)
21 | } else {
22 | self
23 | }
24 | }
25 |
26 | // MARK: - Corner Radius Modifier
27 |
28 | /// Applies a corner radius to specific corners of a view using a custom modifier.
29 | ///
30 | /// - Parameters:
31 | /// - radius: The radius of the corner.
32 | /// - corners: The corners to which the radius should be applied.
33 | ///
34 | /// Example:
35 | /// ```
36 | /// someView.cornerRadius(10, corners: [.topLeft, .bottomRight])
37 | /// ```
38 | ///
39 | /// - Returns: A modified version of the view with the specified corner radius applied.
40 | func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
41 | ModifiedContent(content: self, modifier: SSCornerRadiusStyle(radius: radius, corners: corners))
42 | }
43 |
44 | /// Applies a theme-specific button style to the view using a custom modifier.
45 | ///
46 | /// - Parameters:
47 | /// - txtColor: The color of the text within the button.
48 | /// - font: The font style for the text within the button.
49 | ///
50 | /// Example:
51 | /// ```
52 | /// someView.themeButton(.blue, .headline)
53 | /// ```
54 | ///
55 | /// - Returns: A modified version of the view with the specified text color and font for a themed button.
56 | func themeButton(_ txtColor: Color, _ font: Font) -> some View {
57 | self.modifier(SSThemeButtonModifier(textColor: txtColor, font: font))
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Views/Date picker/SSYearSelectionView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // YearSelectionView.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 24/11/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public struct SSYearSelectionView: View, DatePickerConfigurationDirectAccess {
11 |
12 | // MARK: - Properties
13 |
14 | @Binding var currentView: SelectionView
15 | @EnvironmentObject var calendarManager: SSDatePickerManager
16 | private var gridItem: [GridItem] = Array(repeating: .init(.flexible()), count: SSPickerConstants.monthYearGridRows)
17 |
18 | var configuration: SSDatePickerConfiguration {
19 | calendarManager.configuration
20 | }
21 |
22 | //MARK: - Initializer
23 |
24 | public init(currentView: Binding) {
25 | self._currentView = currentView
26 | }
27 |
28 | //MARK: - Body
29 |
30 | public var body: some View {
31 | yearsGridView
32 | .padding(.top, SSPickerConstants.monthYearViewTopSpace)
33 | .padding(.bottom, SSPickerConstants.monthYearViewBottomSpace)
34 | .onAppear {
35 | calendarManager.updateYearRange(year: (calendarManager.selectedDate ?? calendarManager.currentMonth).year(calendar))
36 | }
37 | }
38 |
39 | //MARK: - Sub views
40 |
41 | private var yearsGridView: some View {
42 | HStack {
43 | LazyVGrid(columns: gridItem, spacing: SSPickerConstants.monthYearGridSpacing) {
44 | ForEach(calendarManager.yearRange, id: \.self) { year in
45 | btnYear(for: year)
46 | }
47 | }
48 | }
49 | }
50 |
51 | @ViewBuilder
52 | private func btnYear(for year: Int) -> some View {
53 | let isSelectedYear = calendarManager.isSelected(year)
54 | Button {
55 | updateYearSelection(year: year)
56 | } label: {
57 | Text(String(year))
58 | .font(isSelectedYear ? selectedYearTextFont : yearTextFont)
59 | .foregroundColor(isSelectedYear ? selectionBackgroundColor : dateMonthYearTextColor)
60 | }
61 | }
62 |
63 | //MARK: - Methods
64 |
65 | private func updateYearSelection(year: Int) {
66 | calendarManager.updateYearSelection(year: year)
67 | currentView = .month
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Views/Time Picker/SSTimeTextField.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SSTimeTextField.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 04/12/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SSTimeTextField: View, TimePickerConfigurationDirectAccess {
11 |
12 | // MARK: - Properties
13 |
14 | @Binding var time: String
15 | var configuration: SSTimePickerConfiguration
16 | @State var isHourField: Bool
17 | @Binding var isInEditMode: Bool
18 | private let charLimit = 2
19 | @FocusState private var isFocused: Bool
20 |
21 | //MARK: - Body
22 |
23 | var body: some View {
24 | Group {
25 | if isInEditMode {
26 | txtField
27 | } else {
28 | timeLabel
29 | .gesture(TapGesture(count: 2).onEnded {
30 | isInEditMode = true
31 | })
32 | }
33 | }
34 | .onChange(of: isInEditMode) { newValue in
35 | isFocused = newValue
36 | }
37 | }
38 |
39 | //MARK: - Sub views
40 |
41 | private var timeLabel: some View {
42 | Text(time.count == 1 ? "0\(time)" : time)
43 | .multilineTextAlignment(.center)
44 | .font(timeLabelFont)
45 | .padding(SSPickerConstants.timeFieldPadding)
46 | .foregroundColor(timeLabelForegroundColor)
47 | .background(timeLabelBackgroundColor)
48 | .fixedSize()
49 | .cornerRadius(SSPickerConstants.timeFieldCornerRadius)
50 | }
51 |
52 | private var txtField: some View {
53 | TextField(isHourField ? SSLocalizedString.hour : SSLocalizedString.minute, text: $time)
54 | .focused($isFocused)
55 | .keyboardType(.numberPad)
56 | .multilineTextAlignment(.center)
57 | .font(timeLabelFont)
58 | .padding(SSPickerConstants.timeFieldPadding)
59 | .foregroundColor(timeLabelForegroundColor)
60 | .background(timeLabelBackgroundColor)
61 | .fixedSize()
62 | .cornerRadius(SSPickerConstants.timeFieldCornerRadius)
63 | .onChange(of: time) { [time] newTime in
64 | if Int(newTime) ?? 00 > (isHourField ? 12 : 59) {
65 | self.time = time
66 | }
67 | if self.time.count > charLimit {
68 | self.time = String(time.prefix(charLimit))
69 | self.time = String(time.prefix(charLimit))
70 | }
71 | }
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Common/SSUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SSUtils.swift
3 | // SSDateTimePicker
4 | //
5 | // Created by Rizwana Desai on 18/12/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | // MARK: - typealias
12 |
13 | public typealias DateRange = (startDate: Date, endDate: Date)
14 | public typealias Time = Date
15 |
16 | // MARK: - SelectionView enum
17 |
18 | /// Enum representing different views available in a datetime picker.
19 | public enum SelectionView {
20 |
21 | /// Date view, allowing selection of day, month, and year.
22 | case date
23 |
24 | /// Month view, allowing selection of month and year.
25 | case month
26 |
27 | /// Year view, allowing selection of the year.
28 | case year
29 |
30 | }
31 |
32 | // MARK: - TimeFormat enum
33 |
34 | /// Enum representing different time formats for a time picker.
35 | enum TimeFormat {
36 | case am
37 | case pm
38 | }
39 |
40 | // MARK: - View style
41 |
42 | public struct SSStyle {
43 | public var font: Font
44 | public var color: Color
45 |
46 | init(font: Font, color: Color) {
47 | self.font = font
48 | self.color = color
49 | }
50 | }
51 |
52 | public struct SSColor {
53 | public var foregroundColor: Color?
54 | public var backgroundColor: Color?
55 |
56 | init(foregroundColor: Color? = nil, backgroundColor: Color? = nil) {
57 | self.foregroundColor = foregroundColor
58 | self.backgroundColor = backgroundColor
59 | }
60 | }
61 |
62 | // MARK: - Font used in SSDateTimePicker
63 |
64 | extension Font {
65 | static let headerTitle: Font = .system(size: Size.headerTitle, weight: .bold)
66 | static let headerDate: Font = .system(size: Size.headerDate, weight: .semibold)
67 | static let weekday: Font = .caption
68 | static let date: Font = .footnote
69 | static let month: Font = .system(size: Size.month, weight: .regular)
70 | static let selectedMonth: Font = .system(size: Size.month, weight: .bold)
71 | static let year: Font = .system(size: Size.year, weight: .regular)
72 | static let selectedYear: Font = .system(size: Size.year, weight: .bold)
73 | static let buttonText: Font = .system(size: Size.buttonsText, weight: .semibold)
74 | static let currentMonthYear: Font = .system(size: Size.currentMonthYear, weight: .medium)
75 | static let clockNumber: Font = .system(size: Size.clockNumber)
76 | static let timeLabel: Font = .system(size: Size.timeLabel, weight: .semibold)
77 | static let timeFormat: Font = .system(size: Size.timeFormat, weight: .bold)
78 | static let selectedTimeFormat: Font = .system(size: Size.selectedTimeFormat, weight: .bold)
79 | }
80 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
42 |
44 |
50 |
51 |
52 |
53 |
59 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Extensions/SSDate+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Date+Extension.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 24/11/23.
6 | //
7 |
8 | import Foundation
9 |
10 |
11 | extension Date {
12 |
13 | // MARK: - Formatted Date Properties
14 |
15 | /// Abbreviated month representation of the date (e.g., "Dec").
16 | public var abbreviatedMonth: String {
17 | DateFormatter.configure(with: DateFormat.abbreviatedMonth).string(from: self)
18 | }
19 |
20 | /// Full date with day of the week, month, and day representation (e.g., "Friday December 3").
21 | public var dayOfWeekWithMonthAndDay: String {
22 | DateFormatter.configure(with: DateFormat.dayOfWeekWithMonthAndDay).string(from: self)
23 | }
24 |
25 | /// Full month representation of the date (e.g., "December").
26 | public var fullMonth: String {
27 | DateFormatter.configure(with: DateFormat.fullMonth).string(from: self)
28 | }
29 |
30 | /// Time-only representation with padding (e.g., "12:45 PM").
31 | public var timeOnlyWithPadding: String {
32 | DateFormatter.configure(with: DateFormat.timeOnlyWithPadding).string(from: self)
33 | }
34 |
35 | /// Month, day, and year representation (e.g., "Jun 3, 2023").
36 | public var monthDateYear: String {
37 | DateFormatter.configure(with: DateFormat.monthDateYear).string(from: self)
38 | }
39 |
40 | /// Month and year representation (e.g., "December, 2023").
41 | public var monthYear: String {
42 | DateFormatter.configure(with: DateFormat.monthYear).string(from: self)
43 | }
44 |
45 | // MARK: - Custom Formatted Date
46 |
47 | /// Returns a custom-formatted string representation of the date.
48 | ///
49 | /// - Parameter format: The format string for the desired date format.
50 | /// - Returns: A string representation of the date in the specified format.
51 | public func formatedString(_ format: String) -> String {
52 | DateFormatter.configure(with: format).string(from: self)
53 | }
54 |
55 | // MARK: - Date Calculations
56 |
57 | /// Returns the date of the next month.
58 | func getNextMonth(_ calender: Calendar) -> Date? {
59 | return calender.date(byAdding: .month, value: 1, to: self)
60 | }
61 |
62 | /// Returns the date of the previous month.
63 | func getPreviousMonth(_ calender: Calendar) -> Date? {
64 | return calender.date(byAdding: .month, value: -1, to: self)
65 | }
66 |
67 | /// Returns the date of the next year.
68 | func getNextYear(_ calender: Calendar) -> Date? {
69 | return calender.date(byAdding: .year, value: 1, to: self)
70 | }
71 |
72 | /// Returns the date of the previous year.
73 | func getPreviousYear(_ calender: Calendar) -> Date? {
74 | return calender.date(byAdding: .year, value: -1, to: self)
75 | }
76 |
77 | /// Returns the year of the date.
78 | ///
79 | /// - Parameter calender: The calendar to use for the date components.
80 | /// - Returns: The year of the date.
81 | func year(_ calender: Calendar) -> Int {
82 | let components = calender.dateComponents([.year], from: self)
83 | return components.year ?? 2023
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Extensions/SSCalendar+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Calendar+Extension.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 24/11/23.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Calendar {
11 |
12 | // MARK: - Generate Dates Inside Interval
13 |
14 | /// Generates an array of dates within the specified date interval that match the given components.
15 | ///
16 | /// - Parameters:
17 | /// - interval: The date interval within which to generate dates.
18 | /// - components: The date components to match for each generated date.
19 | ///
20 | /// Example:
21 | /// ```swift
22 | /// let startDate = Date()
23 | /// let endDate = Calendar.current.date(byAdding: .month, value: 1, to: startDate)!
24 | /// let dateComponents = DateComponents(hour: 12, minute: 0)
25 | /// let generatedDates = Calendar.current.generateDates(inside: DateInterval(start: startDate, end: endDate), matching: dateComponents)
26 | /// ```
27 | ///
28 | /// - Returns: An array of dates within the specified interval that match the provided components.
29 | func generateDates(inside interval: DateInterval,
30 | matching components: DateComponents) -> [Date] {
31 | var dates: [Date] = []
32 | dates.append(interval.start)
33 |
34 | enumerateDates(
35 | startingAfter: interval.start,
36 | matching: components,
37 | matchingPolicy: .nextTime) { date, _, stop in
38 | if let date = date {
39 | if date < interval.end {
40 | dates.append(date)
41 | } else {
42 | stop = true
43 | }
44 | }
45 | }
46 | return dates
47 | }
48 |
49 | // MARK: - First Day of Every Week Date Components
50 |
51 | /// Represents a `DateComponents` instance set to midnight on the first day of the week.
52 | ///
53 | /// Example:
54 | /// ```swift
55 | /// let firstDayOfTheWeek = Calendar.current.firstDayOfEveryWeek
56 | /// // Use `firstDayOfTheWeek` to determine the starting date for a weekly calendar view.
57 | /// ```
58 | ///
59 | /// - Note: The actual first day of the week is determined by the `firstWeekday` property of the calendar.
60 | ///
61 | /// - Returns: A `DateComponents` instance with hour, minute, and second set to zero and the weekday set to the first day of the week.
62 | var firstDayOfEveryWeek: DateComponents {
63 | DateComponents(hour: 0, minute: 0, second: 0, weekday: firstWeekday)
64 | }
65 |
66 | // MARK: - Compare Dates to Granularities
67 |
68 | /// Compares two dates based on specified granularities and determines if they are equal.
69 | ///
70 | /// - Parameters:
71 | /// - date1: The first date to compare.
72 | /// - date2: The second date to compare.
73 | /// - components: The set of granularities to consider in the comparison.
74 | ///
75 | /// Example:
76 | /// ```swift
77 | /// let startDate = Date()
78 | /// let endDate = Calendar.current.date(byAdding: .day, value: 1, to: startDate)!
79 | /// let granularities: Set = [.year, .month, .day]
80 | /// let result = Calendar.current.isDate(startDate, equalTo: endDate, toGranularities: granularities)
81 | /// print(result) // true if the dates are equal up to the specified granularities.
82 | /// ```
83 | ///
84 | /// - Returns: `true` if the dates are equal up to the specified granularities; otherwise, `false`.
85 | func isDate(_ date1: Date, equalTo date2: Date, toGranularities components: Set) -> Bool {
86 | components.reduce(into: true) { isEqual, component in
87 | isEqual = isEqual && isDate(date1, equalTo: date2, toGranularity: component)
88 | }
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Common/SSPickerConstants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SSCalendarConstant.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 23/11/23.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 | import SwiftUI
11 |
12 | let screen = UIScreen.main.bounds
13 |
14 | // MARK: - Picker Constants
15 |
16 | struct SSPickerConstants {
17 |
18 | static let pickerViewWidth: CGFloat = screen.width - (pickerLeadingTrailing*2)
19 | static let pickerViewInnerPadding: CGFloat = 8
20 | static let pickerLeadingTrailing: CGFloat = 40
21 | static let bottomButtonHSpacing: CGFloat = 5
22 | static let widthForDaysOfWeek: CGFloat = (pickerViewWidth - (pickerViewInnerPadding*2)) / 7
23 | static let monthYearGridSpacing: CGFloat = 15
24 | static let horizontalSpacingDates: CGFloat = 0
25 | static let verticleSpacingDates: CGFloat = 0
26 | static let selectionCirclePadding: CGFloat = 5
27 | static let dateRangeSelectionCornerRadius: CGFloat = 11
28 | static let monthYearViewTopSpace: CGFloat = 20
29 | static let monthYearViewBottomSpace: CGFloat = 20
30 | static let verticleSpacingTen: CGFloat = 10
31 | static let paddingTen: CGFloat = 10
32 | static let paddingFive: CGFloat = 5
33 | static let monthYearGridRows: Int = 3
34 | static let rangeSelectionPadding: CGFloat = 2
35 | static let deviderBottomPadding: CGFloat = 8
36 |
37 | // Time picker constant
38 |
39 | static let timeFieldPadding: CGFloat = 8
40 | static let timeFieldCornerRadius: CGFloat = 8
41 | static let circleSize: CGFloat = 40
42 | static let clockPadding: CGFloat = 50
43 | static let clockHeight: CGFloat = 300
44 | static let clockNumberRotationDegree: CGFloat = 30 // To create clock on 360 degree angle, use 30 degree (12*30 = 360)
45 | static let minuteRotationDegree: CGFloat = 6 // 12*5*6 = 360
46 |
47 | }
48 |
49 | // MARK: - Font Size
50 |
51 | struct Size {
52 | static let headerTitle: CGFloat = 12
53 | static let headerDate: CGFloat = 20
54 | static let month: CGFloat = 14
55 | static let year: CGFloat = 14
56 | static let buttonsText: CGFloat = 15
57 | static let currentMonthYear: CGFloat = 14
58 | static let clockNumber: CGFloat = 15
59 | static let timeLabel: CGFloat = 20
60 | static let timeFormat: CGFloat = 12
61 | static let selectedTimeFormat: CGFloat = 15
62 | }
63 |
64 | // MARK: - DateFormat
65 |
66 | /// A struct containing commonly used date format strings for formatting and parsing dates.
67 | public struct DateFormat {
68 |
69 | /// Abbreviated month format (e.g., "Dec").
70 | public static let abbreviatedMonth = "MMM"
71 |
72 | /// Day of the week with month and day format (e.g., "Friday December 3").
73 | public static let dayOfWeekWithMonthAndDay = "EEEE MMMM d"
74 |
75 | /// Full month format (e.g., "December").
76 | public static let fullMonth = "MMMM"
77 |
78 | /// Month and year format (e.g., "December, 2023").
79 | public static let monthYear = "MMMM, yyyy"
80 |
81 | /// Month, day, and year format (e.g., "Jun 3, 2023").
82 | public static let monthDateYear = "MMM d, yyyy"
83 |
84 | /// Day, month, and year format (e.g., "3 Jun, 2023").
85 | public static let dateMonthYear = "d MMM, yyyy"
86 |
87 | /// Full date with day, month, and year format (e.g., "03 December, 2023").
88 | public static let dateMonthYearFull = "dd MMMM, yyyy"
89 |
90 | /// Month, day, and year full format (e.g., "December 03, 2023").
91 | public static let monthDateYearFull = "MMMM dd, yyyy"
92 |
93 | /// Year, month, and day format (e.g., "2023-06-03").
94 | public static let yearMonthDate = "yyyy-MM-dd"
95 |
96 | /// Short month and year format (e.g., "Jun 2023").
97 | public static let shortMonthYear = "MMM yyyy"
98 |
99 | /// Day of the week, day, month, and year format (e.g., "Monday, 3 Jun, 2023").
100 | public static let dayMonthYear = "EEEE, d MMM, yyyy"
101 |
102 | /// Full date format (e.g., "Monday, Jun 03, 2023").
103 | public static let fullDate = "EEEE, MMMM d, yyyy"
104 |
105 | /// Time-only format with padding (e.g., "12:45 PM").
106 | public static let timeOnlyWithPadding = "hh:mm a"
107 |
108 | /// Twenty-four-hour time format (e.g., "12:45").
109 | public static let twentyFourHourFormat = "HH:mm"
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Common/Time picker/SSTimePickerManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SSTimePickerManager.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 04/12/23.
6 | //
7 |
8 | import Foundation
9 |
10 | /// A manager class for handling the state and behavior of the SSTimePicker.
11 | final class SSTimePickerManager: ObservableObject {
12 |
13 | // MARK: - Properties
14 |
15 | /// The configuration for the SSTimePicker.
16 | var configuration: SSTimePickerConfiguration
17 |
18 | /// The selected time. Set this property to pre-select a specific time.
19 | @Published var selectedTime: Date?
20 |
21 | /// The selected time format (AM or PM).
22 | @Published var selectedTimeFromat: TimeFormat = .am
23 |
24 | /// The selected hour as a string.
25 | @Published var hourSelected: String = "12"
26 |
27 | /// The selected minutes as a string.
28 | @Published var minutesSelected: String = "00"
29 |
30 | /// The angle representing the current position of the clock hand.
31 | @Published var angle: Double = 0
32 |
33 | /// A flag indicating whether the minute clock view is currently active.
34 | @Published var isMinuteClock: Bool = false
35 |
36 | var timeSelectionCallback: (Date) -> () = {_ in}
37 |
38 | // MARK: - Initializer
39 |
40 | /// Initializes the SSTimePickerManager with the provided configuration.
41 | ///
42 | /// - Parameter configuration: The style configuration for the SSTimePicker.
43 | public init(configuration: SSTimePickerConfiguration = SSTimePickerConfiguration()) {
44 | self.configuration = configuration
45 | }
46 |
47 | //MARK: - Methods
48 |
49 | /// Switches the time picker to show the hour clock view and updates the current hour angle.
50 | func actionShowHourClock() {
51 | updateCurrentHourAngle()
52 | isMinuteClock = false
53 | }
54 |
55 | /// Switches the time picker to show the minute clock view and updates the current minute angle.
56 | func actionShowMinuteClock() {
57 | updateCurrentMinuteAngle()
58 | isMinuteClock = true
59 | }
60 |
61 | /// Updates the angle based on the current selected hour.
62 | func updateCurrentHourAngle() {
63 | let hour = updateWithDefaultHourIfEmpty()
64 | angle = Double(hour * 30)
65 | }
66 |
67 | /// Updates the angle based on the current selected minute.
68 | func updateCurrentMinuteAngle() {
69 | let minute = updateWithDefaultMinuteIfEmpty()
70 | angle = Double(minute * 6)
71 | }
72 |
73 | /// Updates the selected time based on the current hour, minute, and time format.
74 | func updateSelectedTime() {
75 | _ = updateWithDefaultMinuteIfEmpty()
76 | _ = updateWithDefaultHourIfEmpty()
77 | let format = DateFormatter.configure(with: DateFormat.twentyFourHourFormat)
78 | // get hours for 24-hrs format
79 | let hour = (Int(hourSelected) ?? 12)
80 | let updatedHour = selectedTimeFromat == .am ? (hour == 12 ? 00.formattedTime : hour.formattedTime) : "\(hour < 12 ? (hour + 12).formattedTime : hour.formattedTime)"
81 | let date = format.date(from: "\(updatedHour):\(minutesSelected)")
82 | selectedTime = date
83 | handleCallback()
84 | isMinuteClock = false
85 | }
86 |
87 | // In case if textfield is empty set default minute
88 | func updateWithDefaultMinuteIfEmpty() -> Int {
89 | let minute = Int(minutesSelected) ?? 00
90 | if minutesSelected == "" {
91 | minutesSelected = "00"
92 | }
93 | return minute
94 | }
95 |
96 | // In case if textfield is empty set default hour
97 | func updateWithDefaultHourIfEmpty() -> Int {
98 | let hour = Int(hourSelected) ?? 12
99 | if hourSelected == "" {
100 | hourSelected = "12"
101 | }
102 | return hour
103 | }
104 |
105 | /// Sets up the initial time, angle, and clock view based on the selected time.
106 | func setUpTimeAndAngle() {
107 | let calender = Calendar.current
108 | guard let selectedTime else { return }
109 | // 24 Hrs
110 | var hourTemp = calender.component(.hour, from: selectedTime)
111 | selectedTimeFromat = hourTemp <= 12 ? .am : .pm
112 | hourTemp = hourTemp == 0 ? 12 : hourTemp
113 | hourTemp = hourTemp <= 12 ? hourTemp: hourTemp - 12
114 | let minutesTemp = calender.component(.minute, from: selectedTime)
115 | hourSelected = hourTemp.formattedTime
116 | minutesSelected = minutesTemp.formattedTime
117 | updateCurrentHourAngle()
118 | }
119 |
120 | /// Notifies the delegate when a time is selected.
121 | func handleCallback() {
122 | if let selectedTime {
123 | timeSelectionCallback(selectedTime)
124 | }
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Common/Time picker/SSTimePickerConfiguration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SSTimePickerConfiguration.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 04/12/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | /// A configuration structure for customizing the appearance and behavior of the SSTimePicker.
12 | struct SSTimePickerConfiguration {
13 |
14 | // MARK: - Colors and fonts
15 |
16 | /// Color for the header title "Select Time".
17 | var headerTitleStyle: SSStyle = .init(font: Font.headerTitle, color: .gray)
18 |
19 | /// Text color for the selected and unselected number on the clock.
20 | var clockNumberStyle: SSStyle = .init(font: Font.clockNumber, color: .black)
21 |
22 | /// Font for Hour (HH) and Minute (MM) labels.
23 | var timeLabelStyle: SSStyle = .init(font: Font.timeLabel, color: .darkPink)
24 |
25 | /// Font for the AM/PM time format when unselected.
26 | var timeFormatStyle: SSStyle = .init(font: Font.timeFormat, color: .gray)
27 |
28 | /// Font for the AM/PM time format when selected.
29 | var selectedTimeFormatStyle: SSStyle = .init(font: Font.selectedTimeFormat, color: .darkPink)
30 |
31 | var buttonStyle: SSStyle = .init(font: Font.selectedTimeFormat, color: .darkPink)
32 |
33 | // MARK: - Colors
34 |
35 | /// Color for the clock hand and circular selection circle.
36 | var clockHandColor: Color
37 |
38 | /// Background and foreground color for Hour (HH) and Minute (MM) labels.
39 | var timeLabelBackgroundColor: Color = .peach
40 |
41 | /// Color for the overlay background of the popup.
42 | var popupOverlayColor: Color = Color.black.opacity(0.5)
43 |
44 | /// Background color for the picker.
45 | var pickerBackgroundColor: Color
46 |
47 | // MARK: - Additional Properties
48 |
49 | /// Radius for the corner of the picker view.
50 | var pickerViewRadius: CGFloat = 15
51 |
52 | // MARK: - Initializer
53 |
54 | /// Creates a custom-themed time picker configuration.
55 | ///
56 | /// - Parameters:
57 | /// - pickerBackgroundColor: The background color of the picker view. Default is light pink.
58 | /// - primaryColor: The color for the selected time format (AM/PM), buttons text color, time text (HH:MM), and clock hand color.
59 | /// - timeLabelBackgroundColor: The background color for the time label (HH:MM). Default is light pink.
60 | ///
61 | /// Use this instance to set up the appearance and behavior of the SSTimePicker according to your preferences.
62 | init(pickerBackgroundColor: Color = Color.lightPink, primaryColor: Color = Color.darkPink, timeLabelBackgroundColor: Color = Color.peach) {
63 | self.timeLabelStyle.color = primaryColor
64 | self.selectedTimeFormatStyle.color = primaryColor
65 | self.buttonStyle.color = primaryColor
66 | self.clockHandColor = primaryColor
67 | self.pickerBackgroundColor = pickerBackgroundColor
68 | self.timeLabelBackgroundColor = timeLabelBackgroundColor
69 | }
70 |
71 | }
72 |
73 | /// A protocol providing direct access to specific properties of an `SSTimePickerConfiguration`.
74 | protocol TimePickerConfigurationDirectAccess {
75 |
76 | var configuration: SSTimePickerConfiguration { get }
77 |
78 | }
79 |
80 | extension TimePickerConfigurationDirectAccess {
81 |
82 | var timeLabelBackgroundColor: Color {
83 | configuration.timeLabelBackgroundColor
84 | }
85 |
86 | var timeLabelForegroundColor: Color {
87 | configuration.timeLabelStyle.color
88 | }
89 |
90 | var timeFormatSelectionColor: Color {
91 | configuration.selectedTimeFormatStyle.color
92 | }
93 |
94 | var timeFormatColor: Color {
95 | configuration.timeFormatStyle.color
96 | }
97 |
98 | var headerTitleColor: Color {
99 | configuration.headerTitleStyle.color
100 | }
101 |
102 | var buttonsForegroundColor: Color {
103 | configuration.buttonStyle.color
104 | }
105 |
106 | var pickerBackgroundColor: Color {
107 | configuration.pickerBackgroundColor
108 | }
109 |
110 | var popupOverlayColor: Color {
111 | configuration.popupOverlayColor
112 | }
113 |
114 | var pickerViewRadius: CGFloat {
115 | configuration.pickerViewRadius
116 | }
117 |
118 | var clockHandColor: Color {
119 | configuration.clockHandColor
120 | }
121 |
122 | var clockNumberTextColor: Color {
123 | configuration.clockNumberStyle.color
124 | }
125 |
126 | var headerTitleFont: Font {
127 | configuration.headerTitleStyle.font
128 | }
129 |
130 | var timeLabelFont: Font {
131 | configuration.timeLabelStyle.font
132 | }
133 |
134 | var timeFormatFont: Font {
135 | configuration.timeFormatStyle.font
136 | }
137 |
138 | var selectedTimeFormatFont: Font {
139 | configuration.selectedTimeFormatStyle.font
140 | }
141 |
142 | var clockNumberFont: Font {
143 | configuration.clockNumberStyle.font
144 | }
145 |
146 | var buttonFont: Font {
147 | configuration.buttonStyle.font
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Views/Date picker/SSDateView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DateView.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 23/11/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SSDateView: View, DatePickerConfigurationDirectAccess {
11 |
12 | // MARK: - Properties
13 |
14 | @EnvironmentObject var calendarManager: SSDatePickerManager
15 | var date: Date
16 | var weekDateInSelectedMonth: Date
17 |
18 | var configuration: SSDatePickerConfiguration {
19 | calendarManager.configuration
20 | }
21 |
22 | private var isDayToday: Bool {
23 | calendar.isDateInToday(date)
24 | }
25 |
26 | private var isDaySelectableAndInRange: Bool {
27 | isDayWithinDateRange && isDayWithinWeekMonthAndYear && canSelectDay
28 | }
29 |
30 | private var isDayWithinDateRange: Bool {
31 | guard let minimumDate, let maximumDate else { return true }
32 | return date >= calendar.startOfDay(for: minimumDate) && date <= maximumDate
33 | }
34 |
35 | private var isDayWithinWeekMonthAndYear: Bool {
36 | calendar.isDate(weekDateInSelectedMonth, equalTo: date, toGranularities: [.month, .year])
37 | }
38 |
39 | private var canSelectDay: Bool {
40 | calendarManager.canSelectDate(date)
41 | }
42 |
43 | private var isSelected: Bool {
44 | if allowMultipleSelection {
45 | return isSelectedDatesContainSameDate
46 | } else {
47 | return isSelectedDateSame
48 | }
49 | }
50 |
51 | private var numericDay: String {
52 | String(calendar.component(.day, from: date))
53 | }
54 |
55 | private var foregroundColor: Color {
56 | if isDayToday {
57 | return isSelected && isDaySelectableAndInRange ? todaySelectionFontColor : todayColor
58 | } else if isSelected && isDaySelectableAndInRange {
59 | return selectedDateTextColor
60 | } else {
61 | return dateMonthYearTextColor
62 | }
63 | }
64 |
65 | private var backgroundColor: Color {
66 | if isDayToday && isSelected {
67 | return todaySelectionBgColor
68 | } else if isSelected || isStartDate || isEndDate {
69 | return selectionBackgroundColor
70 | } else if isDayWithinSelectedDateRange {
71 | return selectionBackgroundColor.opacity(0.3)
72 | } else {
73 | return Color.clear
74 | }
75 | }
76 |
77 | private var opacity: Double {
78 | return isDaySelectableAndInRange ? 1 : 0.15
79 | }
80 |
81 | private var isSelectedDateSame: Bool {
82 | guard let selectedDate = calendarManager.selectedDate else { return false }
83 | return calendar.isDate(selectedDate, equalTo: date, toGranularities: [.day, .month, .year])
84 | }
85 |
86 | private var isSelectedDatesContainSameDate: Bool {
87 | guard let selectedDates = calendarManager.selectedDates else { return false }
88 | for selectedDate in selectedDates {
89 | if calendar.isDate(selectedDate, equalTo: date, toGranularities: [.day, .month, .year]) {
90 | return true
91 | }
92 | }
93 | return false
94 | }
95 |
96 | private var isDayWithinSelectedDateRange: Bool {
97 | guard let startDate = calendarManager.startDate, let endDate = calendarManager.endDate else { return false }
98 | return date >= calendar.startOfDay(for: startDate) && date <= endDate
99 | }
100 |
101 | private var isStartDate: Bool {
102 | guard let startDate = calendarManager.startDate else { return false }
103 | return calendar.isDate(startDate, equalTo: date, toGranularities: [.day, .month, .year])
104 | }
105 |
106 | private var isEndDate: Bool {
107 | guard let endDate = calendarManager.endDate else { return false }
108 | return calendar.isDate(endDate, equalTo: date, toGranularities: [.day, .month, .year])
109 | }
110 |
111 | private var corners: UIRectCorner {
112 | isStartDate ? [.topLeft,.bottomLeft] : [.topRight,.bottomRight]
113 | }
114 |
115 | //MARK: - Initializer
116 |
117 | init(date: Date, weekDateInSelectedMonth: Date) {
118 | self.date = date
119 | self.weekDateInSelectedMonth = weekDateInSelectedMonth
120 | }
121 |
122 | //MARK: - Body
123 |
124 | var body: some View {
125 | lblDate
126 | }
127 |
128 | //MARK: - Sub views
129 |
130 | private var lblDate: some View {
131 | Text(numericDay)
132 | .font(dateTextFont)
133 | .foregroundColor(foregroundColor)
134 | .frame(width: SSPickerConstants.widthForDaysOfWeek, height: SSPickerConstants.widthForDaysOfWeek)
135 | .if(!allowRangeSelection && isDaySelectableAndInRange, transform: { view in
136 | view
137 | .background(Circle()
138 | .padding(.leading, SSPickerConstants.selectionCirclePadding)
139 | .padding(.trailing, SSPickerConstants.selectionCirclePadding)
140 | .foregroundColor(backgroundColor))
141 | })
142 | .if(allowRangeSelection, transform: { view in
143 | view
144 | .background(backgroundColor)
145 | .cornerRadius((isStartDate || isEndDate) ? SSPickerConstants.dateRangeSelectionCornerRadius : 0, corners: corners)
146 | .padding(.top, SSPickerConstants.rangeSelectionPadding)
147 | .padding(.bottom, SSPickerConstants.rangeSelectionPadding)
148 | })
149 | .opacity(opacity)
150 | .onTapGesture(perform: updateSelection)
151 | }
152 |
153 | //MARK: - Methods
154 |
155 | private func updateSelection() {
156 | guard isDaySelectableAndInRange else { return }
157 | self.calendarManager.updateDateSelection(date: date)
158 | }
159 |
160 | }
161 |
--------------------------------------------------------------------------------
/Example/Example/View/PickerExampleView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PickerExampleView.swift
3 | // Example
4 | //
5 | // Created by Rizwana Desai on 22/11/23.
6 | //
7 |
8 | import SwiftUI
9 | import SSDateTimePicker
10 |
11 | struct PickerExampleView: View {
12 |
13 | //MARK: - Properties
14 |
15 | @State var showDatePicker: Bool = false
16 | @State var showMultiDatePicker: Bool = false
17 | @State var showDateRangePicker: Bool = false
18 | @State var showTimePicker: Bool = false
19 | @State var displayCustomizedCalendar: Bool = false
20 | @State var selectedDate: Date = Date()
21 | @State var selectedDateCombine: Date?
22 | @ObservedObject var pickerViewModel = PickerViewModel()
23 |
24 | //MARK: - Body
25 | var body: some View {
26 | ZStack {
27 | dateTimePickerExampleView
28 | datePicker
29 | multiDatePicker
30 | dateRangePicker
31 | timePicker
32 | }
33 |
34 | }
35 |
36 | //MARK: - Sub views
37 |
38 | var headerView: some View {
39 | Text(LocalizedString.dateTimePickerExample)
40 | .font(.system(size: 22, weight: .bold))
41 | .foregroundColor(.white)
42 | .frame(maxWidth: .infinity, maxHeight: 60)
43 | .background(Color.darkPink.ignoresSafeArea())
44 | }
45 |
46 | var dateTimePickerExampleView: some View {
47 | VStack {
48 | headerView
49 | ScrollView {
50 | VStack(alignment: .center, spacing: 10) {
51 | singleDateSelectionView
52 | multipleDateSelectionView
53 | dateRangeSelectionView
54 | timePickerView
55 | }
56 | }
57 | }
58 | .background(Color.white)
59 | .foregroundStyle(.black)
60 | }
61 |
62 | var singleDateSelectionView: some View {
63 | VStack(spacing: 10) {
64 | Text("\(LocalizedString.singleDateSelectionExample)")
65 | .font(.callout)
66 | Text("\(LocalizedString.selectedDate) \(pickerViewModel.selectedDate?.monthDateYear ?? "-")")
67 | .font(.footnote)
68 | btnSelectSingleDate
69 | }
70 | .themeCard()
71 | }
72 |
73 | var multipleDateSelectionView: some View {
74 | VStack(spacing: 10) {
75 | Text("\(LocalizedString.multipleDateSelectionExample)")
76 | .font(.callout)
77 | Text("\(LocalizedString.selectedDates) \(pickerViewModel.getSelectedDates() ?? "-")")
78 | .font(.footnote)
79 | btnSelectMultipleDates
80 | }
81 | .themeCard()
82 | }
83 |
84 | var dateRangeSelectionView: some View {
85 | VStack(spacing: 10) {
86 | Text("\(LocalizedString.dateRangeSelectionExample)")
87 | .font(.callout)
88 | Text("\(LocalizedString.startDate) \(pickerViewModel.selectedDateRange?.startDate.monthDateYear ?? "-"), \(LocalizedString.endDate) \(pickerViewModel.selectedDateRange?.endDate.monthDateYear ?? "-")")
89 | .font(.footnote)
90 | btnSelectDateRange
91 | }
92 | .themeCard()
93 | }
94 |
95 | var btnSelectDateRange: some View {
96 | Button {
97 | withAnimation {
98 | showDateRangePicker.toggle()
99 | }
100 | } label: {
101 | Text(LocalizedString.selectDateRange)
102 | .themeButton()
103 | }
104 | }
105 |
106 | var timePickerView: some View {
107 | VStack(spacing: 10) {
108 | Text("\(LocalizedString.timePickerExample)")
109 | .font(.callout)
110 | Text("\(LocalizedString.selectedTime) \(pickerViewModel.selectedTime?.timeOnlyWithPadding ?? "-")")
111 | .font(.footnote)
112 | btnTimePicker
113 | }
114 | .themeCard()
115 | }
116 |
117 | var btnTimePicker: some View {
118 | Button {
119 | withAnimation {
120 | showTimePicker.toggle()
121 | }
122 | } label: {
123 | Text(LocalizedString.selectTime)
124 | .themeButton()
125 | }
126 | }
127 |
128 | var btnSelectSingleDate: some View {
129 | Button {
130 | withAnimation {
131 | showDatePicker.toggle()
132 | }
133 | } label: {
134 | Text(LocalizedString.selectSingleDate)
135 | .themeButton()
136 | }
137 | }
138 |
139 | var btnSelectMultipleDates: some View {
140 | Button {
141 | withAnimation {
142 | showMultiDatePicker.toggle()
143 | }
144 | } label: {
145 | Text(LocalizedString.selectMultipleDate)
146 | .themeButton()
147 | }
148 | }
149 |
150 | var datePicker: some View {
151 | SSDatePicker(showDatePicker: $showDatePicker)
152 | .selectedDate(self.pickerViewModel.selectedDate)
153 | .onDateSelection({ date in
154 | self.pickerViewModel.selectedDate = date
155 | })
156 | }
157 |
158 | var dateRangePicker: some View {
159 | SSDatePicker(showDatePicker: $showDateRangePicker)
160 | .enableDateRangeSelection()
161 | .selectedDateRange(pickerViewModel.selectedDateRange)
162 | .onDateRangeSelection({ dateRange in
163 | pickerViewModel.selectedDateRange = dateRange
164 | })
165 | }
166 |
167 | var multiDatePicker: some View {
168 | SSDatePicker(showDatePicker: $showMultiDatePicker)
169 | .enableMultipleDateSelection()
170 | .selectedDates(pickerViewModel.selectedDates)
171 | .onMultiDateSelection({ dates in
172 | pickerViewModel.selectedDates = dates
173 | })
174 | }
175 |
176 | var timePicker: some View {
177 | SSTimePicker(showTimePicker: $showTimePicker)
178 | .selectedTime(pickerViewModel.selectedTime)
179 | .onTimeSelection { time in
180 | pickerViewModel.selectedTime = time
181 | }
182 | }
183 |
184 | }
185 |
186 | struct ContentView_Previews: PreviewProvider {
187 | static var previews: some View {
188 | PickerExampleView()
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Views/Time Picker/SSClockPicker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SSClockPicker.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 04/12/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SSClockPicker: View, TimePickerConfigurationDirectAccess {
11 |
12 | // MARK: - Properties
13 |
14 | @ObservedObject var timePickerManager: SSTimePickerManager
15 | private var threeSixtyDegree: CGFloat = 360
16 | private var oneEightyDegree: CGFloat = 180
17 | var configuration: SSTimePickerConfiguration {
18 | timePickerManager.configuration
19 | }
20 |
21 | //MARK: - Initializer
22 |
23 | init(timePickerManager: SSTimePickerManager) {
24 | self.timePickerManager = timePickerManager
25 | }
26 |
27 | //MARK: - Body
28 |
29 | var body: some View {
30 | GeometryReader { reader in
31 | ZStack {
32 | let widthGeo = reader.frame(in: .global).width/2
33 | selectionCircle(widthGeo)
34 | clockHand(widthGeo)
35 | clockFace(widthGeo)
36 | }
37 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
38 | }
39 | .frame(height: SSPickerConstants.clockHeight)
40 | }
41 |
42 | //MARK: - Sub views
43 |
44 | private func selectionCircle(_ widthGeo: CGFloat) -> some View {
45 | Circle()
46 | .fill(clockHandColor)
47 | .frame(width: SSPickerConstants.circleSize, height: SSPickerConstants.circleSize)
48 | .offset(x: widthGeo - SSPickerConstants.clockPadding)
49 | .rotationEffect(.init(degrees: timePickerManager.angle))
50 | .simultaneousGesture(DragGesture().onChanged(onChanged(value:)).onEnded(onEnd(value:)))
51 | .rotationEffect(.init(degrees: -90))
52 | }
53 |
54 | private func clockFace(_ widthGeo: CGFloat) -> some View {
55 | ForEach(1...12, id: \.self) { index in
56 | VStack {
57 | let time = timePickerManager.isMinuteClock ? index*5 : index
58 | Text("\(time)")
59 | .font(clockNumberFont)
60 | .fontWeight(.semibold)
61 | .foregroundColor(clockNumberTextColor)
62 | .rotationEffect(.init(degrees: Double(-index)*SSPickerConstants.clockNumberRotationDegree))
63 | .highPriorityGesture(TapGesture().onEnded {
64 | actionClockNumberSelection(number: index)
65 | }).allowsHitTesting(isClockNumberTapable("\(time)"))
66 | }
67 | .offset(y: -widthGeo+SSPickerConstants.clockPadding)
68 | .rotationEffect(.init(degrees: Double(index)*SSPickerConstants.clockNumberRotationDegree)) // rotating view : 12*30 = 360
69 | }
70 | }
71 |
72 | private func clockHand(_ widthGeo: CGFloat) -> some View {
73 | Circle()
74 | .fill(clockHandColor)
75 | .frame(width: SSPickerConstants.paddingTen, height: SSPickerConstants.paddingTen)
76 | .overlay (
77 | Rectangle()
78 | .fill(clockHandColor)
79 | .frame(width: 2, height: clockHandHeight(widthGeo))
80 | , alignment: .bottom
81 | )
82 | .rotationEffect(.init(degrees: timePickerManager.angle))
83 | }
84 |
85 | //MARK: - Methods
86 |
87 | private func clockHandHeight(_ width: CGFloat) -> CGFloat {
88 | width-SSPickerConstants.clockPadding
89 | }
90 |
91 | // To disable clock number tap gesture when it is already selected to priotize grag gesture
92 | private func isClockNumberTapable(_ time: String) -> Bool {
93 | if timePickerManager.isMinuteClock {
94 | let minute = time == "60" ? "00" : time
95 | return timePickerManager.minutesSelected != minute
96 | } else {
97 | return timePickerManager.hourSelected != time
98 | }
99 | }
100 |
101 | // To update selected hour/miute when user tap on any of the number on clock
102 | private func actionClockNumberSelection(number: Int) {
103 | if timePickerManager.isMinuteClock {
104 | let minute = number * 5
105 | timePickerManager.minutesSelected = (minute == 60 ? 00 : minute).formattedTime
106 | timePickerManager.updateCurrentMinuteAngle()
107 | } else {
108 | timePickerManager.hourSelected = number.formattedTime
109 | timePickerManager.updateCurrentHourAngle()
110 | }
111 | }
112 |
113 | }
114 |
115 | //MARK: - Drag gesture methods
116 |
117 | extension SSClockPicker {
118 |
119 | private func onChanged(value: DragGesture.Value) {
120 | // getting angle
121 | let vector = CGVector(dx: value.location.x
122 | , dy: value.location.y)
123 | let radians = atan2(vector.dy, vector.dx)
124 | var angle = radians * oneEightyDegree / .pi
125 | if angle < 0 {
126 | angle = threeSixtyDegree + angle
127 | }
128 | timePickerManager.angle = angle
129 | if !timePickerManager.isMinuteClock {
130 | // updating angle for hour if the angle is in between hours
131 | let roundValue = Int(SSPickerConstants.clockNumberRotationDegree) * Int(round(angle/SSPickerConstants.clockNumberRotationDegree))
132 | timePickerManager.angle = Double(roundValue)
133 | updateHour()
134 | } else {
135 | // updating minutes
136 | let progress = angle / threeSixtyDegree
137 | timePickerManager.minutesSelected = Int(progress*60).formattedTime
138 | }
139 | }
140 |
141 | private func onEnd(value: DragGesture.Value) {
142 | if !timePickerManager.isMinuteClock {
143 | updateHour()
144 | // updating picker to minutes
145 | withAnimation {
146 | timePickerManager.angle = Double((Int(timePickerManager.minutesSelected) ?? 00) * Int(SSPickerConstants.minuteRotationDegree))
147 | timePickerManager.isMinuteClock = true
148 | }
149 | }
150 | }
151 |
152 | // updating hour value
153 | private func updateHour() {
154 | let hour = Int(timePickerManager.angle / SSPickerConstants.clockNumberRotationDegree)
155 | timePickerManager.hourSelected = (hour == 0 ? 12 : hour).formattedTime
156 | }
157 |
158 | }
159 |
160 |
161 | struct SSClockPicker_Previews: PreviewProvider {
162 | static var previews: some View {
163 | SSClockPicker(timePickerManager: SSTimePickerManager(configuration: SSTimePickerConfiguration()))
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Common/Date picker/SSDatePickerConfiguration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SSCalendarConfiguration.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 23/11/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | /// A configuration structure for customizing the appearance and behavior of the SSDatePicker.
12 | struct SSDatePickerConfiguration {
13 |
14 | // MARK: - Colors and fonts
15 |
16 | /// Color and font for the header title ("Select date").
17 | var headerTitleStyle: SSStyle = .init(font: .headerTitle, color: .gray)
18 |
19 | /// Color and font for the header date text.
20 | var headerDateStyle: SSStyle = .init(font: .headerDate, color: .black)
21 |
22 | /// Text color and font for weekdays in the picker.
23 | var weekdayStyle: SSStyle = .init(font: .weekday, color: .gray)
24 |
25 | /// Text color and font for date in the picker.
26 | var dateStyle: SSStyle = .init(font: .date, color: .black)
27 |
28 | /// Text color and font for month text.
29 | var monthStyle: SSStyle = .init(font: .month, color: .black)
30 |
31 | /// Text color and font for selected month text.
32 | var selectedMonthStyle: SSStyle = .init(font: .selectedMonth, color: .darkPink)
33 |
34 | /// Text color and font for year text.
35 | var yearStyle: SSStyle = .init(font: .year, color: .black)
36 |
37 | /// Text color and font for selected year text.
38 | var selectedYearStyle: SSStyle = .init(font: .selectedYear, color: .darkPink)
39 |
40 | /// Font and foreground color for buttons.
41 | var buttonStyle: SSStyle = .init(font: .buttonText, color: .darkPink)
42 |
43 | /// Font and color for the current month and year label at the bottom of the picker.
44 | var currentMonthYearLabelStyle: SSStyle = .init(font: .currentMonthYear, color: .black)
45 |
46 | /// Selected date foreground and background color.
47 | var selectedDateColor: SSColor = .init(foregroundColor: .white, backgroundColor: .darkPink)
48 |
49 | /// Foreground and background color for today's date text.
50 | var todayDateColor: SSColor = .init(foregroundColor: nil, backgroundColor: nil)
51 |
52 | /// Background and foreground color for today's selected date.
53 | var todayDateSelectionColor: SSColor = .init(foregroundColor: nil, backgroundColor: nil)
54 |
55 | /// Background color for the picker.
56 | var pickerBackgroundColor: Color
57 |
58 | /// Color for the separator lines.
59 | var sepratorLineColor: Color = Color(uiColor: UIColor.opaqueSeparator)
60 |
61 | /// Color for the overlay background of the popup.
62 | var popupOverlayColor: Color = Color.black.opacity(0.5)
63 |
64 | // MARK: - Date Selection Behavior Configuration Properties
65 |
66 | /// Allow selecting multiple dates.
67 | var allowMultipleSelection: Bool = false
68 |
69 | /// Allow selecting a range of dates.
70 | var allowRangeSelection: Bool = false
71 |
72 | /// The minimum selectable date. Dates before this minimum date will be disabled for selection.
73 | var minimumDate: Date?
74 |
75 | /// The maximum selectable date. Dates after this maximum date will be disabled for selection.
76 | var maximumDate: Date?
77 |
78 | /// Calendar to use for date calculations.
79 | var calendar: Calendar = Calendar.current
80 |
81 | // MARK: - Additional Properties
82 |
83 | /// Radius for the corner of the picker view.
84 | var pickerViewRadius: CGFloat = 15
85 |
86 | /// Date format for the header.
87 | var headerDateFormat: String = DateFormat.monthDateYear
88 |
89 | // MARK: - Initializer
90 |
91 | /// Creates a custom-themed date picker configuration.
92 | ///
93 | /// - Parameters:
94 | /// - pickerBackgroundColor: The background color of the picker view. Default is light pink.
95 | /// - primaryColor: The color for both the selected date's background and the buttons. Deafult is dark pink.
96 | ///
97 | /// Use this instance to set up the appearance and behavior of the SSDatePicker according to your preferences.
98 | init(pickerBackgroundColor: Color = Color.lightPink, primaryColor: Color = Color.darkPink) {
99 | self.selectedDateColor.backgroundColor = primaryColor
100 | self.buttonStyle.color = primaryColor
101 | self.pickerBackgroundColor = pickerBackgroundColor
102 | }
103 | }
104 |
105 |
106 | protocol DatePickerConfigurationDirectAccess {
107 |
108 | var configuration: SSDatePickerConfiguration { get }
109 |
110 | }
111 |
112 | extension DatePickerConfigurationDirectAccess {
113 |
114 | var calendar: Calendar {
115 | configuration.calendar
116 | }
117 |
118 | var headerTitleColor: Color {
119 | configuration.headerTitleStyle.color
120 | }
121 |
122 | var headerDateColor: Color {
123 | configuration.headerDateStyle.color
124 | }
125 |
126 | var weekdayTextColor: Color {
127 | configuration.weekdayStyle.color
128 | }
129 |
130 | var dateMonthYearTextColor: Color {
131 | configuration.dateStyle.color
132 | }
133 |
134 | var selectedDateTextColor: Color {
135 | configuration.selectedDateColor.foregroundColor ?? dateMonthYearTextColor
136 | }
137 |
138 | var selectionBackgroundColor: Color {
139 | configuration.selectedDateColor.backgroundColor ?? .darkPink
140 | }
141 |
142 | var todayColor: Color {
143 | configuration.todayDateColor.foregroundColor ?? dateMonthYearTextColor
144 | }
145 |
146 | var todaySelectionBgColor: Color {
147 | configuration.todayDateSelectionColor.backgroundColor ?? selectionBackgroundColor
148 | }
149 |
150 | var todaySelectionFontColor: Color {
151 | configuration.todayDateSelectionColor.foregroundColor ?? dateMonthYearTextColor
152 | }
153 |
154 | var buttonsForegroundColor: Color {
155 | configuration.buttonStyle.color
156 | }
157 |
158 | var pickerBackgroundColor: Color {
159 | configuration.pickerBackgroundColor
160 | }
161 |
162 | // date format
163 | var headerDateFormat: String {
164 | configuration.headerDateFormat
165 | }
166 |
167 | var pickerViewRadius: CGFloat {
168 | configuration.pickerViewRadius
169 | }
170 |
171 | var nextPrevButtonColor: Color {
172 | configuration.buttonStyle.color
173 | }
174 |
175 | var monthYearNavigationLabelColor: Color {
176 | configuration.currentMonthYearLabelStyle.color
177 | }
178 |
179 | var allowMultipleSelection: Bool {
180 | configuration.allowMultipleSelection
181 | }
182 |
183 | var allowRangeSelection: Bool {
184 | configuration.allowRangeSelection
185 | }
186 |
187 | var minimumDate: Date? {
188 | configuration.minimumDate
189 | }
190 |
191 | var maximumDate: Date? {
192 | configuration.maximumDate
193 | }
194 |
195 | var popupOverlayColor: Color {
196 | configuration.popupOverlayColor
197 | }
198 |
199 | var headerTitleFont: Font {
200 | configuration.headerTitleStyle.font
201 | }
202 |
203 | var headerDateFont: Font {
204 | configuration.headerDateStyle.font
205 | }
206 |
207 | var weekdayTextFont: Font {
208 | configuration.weekdayStyle.font
209 | }
210 |
211 | var dateTextFont: Font {
212 | configuration.dateStyle.font
213 | }
214 |
215 | var monthTextFont: Font {
216 | configuration.monthStyle.font
217 | }
218 |
219 | var selectedMonthTextFont: Font {
220 | configuration.selectedMonthStyle.font
221 | }
222 |
223 | var yearTextFont: Font {
224 | configuration.yearStyle.font
225 | }
226 |
227 | var buttonsFont: Font {
228 | configuration.buttonStyle.font
229 | }
230 |
231 | var currentMonthYearBottomLabelFont: Font {
232 | configuration.currentMonthYearLabelStyle.font
233 | }
234 |
235 | var selectedYearTextFont: Font {
236 | configuration.selectedYearStyle.font
237 | }
238 |
239 | var sepratorLineColor: Color {
240 | configuration.sepratorLineColor
241 | }
242 |
243 | }
244 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Common/Date picker/SSDatePickerManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SSCalendarManager.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 27/11/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 | import Combine
11 |
12 | /// A manager class for handling the state and behavior of the SSDatePicker.
13 | final class SSDatePickerManager: ObservableObject, DatePickerConfigurationDirectAccess {
14 |
15 | // MARK: - Properties
16 |
17 | /// The current month displayed in the date picker.
18 | /// Setting this property will determine the month whose calendar dates will be displayed when the picker is opened.
19 | @Published var currentMonth: Date
20 |
21 | /// The selected date in the date picker. Set this property to pre-select a specific date.
22 | @Published var selectedDate: Date? = nil
23 |
24 | /// The selected dates in the date picker when multiple selection is allowed. Set this property to pre-select a specific dates.
25 | @Published var selectedDates: [Date]? = nil
26 |
27 | /// The start date for range selection in the date picker.
28 | @Published var startDate: Date? = nil
29 |
30 | /// The end date for range selection in the date picker.
31 | @Published var endDate: Date? = nil
32 |
33 | /// List of dates that needs to be disabled
34 | var disableDates: [Date]?
35 |
36 | /// The configuration for the SSDatePicker.
37 | public var configuration: SSDatePickerConfiguration
38 |
39 | /// The year range displayed in the year selection view.
40 | @Published private(set) var yearRange = [Int]()
41 |
42 | /// The last known current month for canceling selection.
43 | private var lastCurrentMonth: Date
44 |
45 | /// The last known selected date for canceling selection.
46 | private var lastSelectedDate: Date?
47 |
48 | var dateRangeSelectionCallback: (DateRange) -> () = {_ in}
49 | var multiDateSelectionCallback: ([Date]) -> () = {_ in}
50 | var dateSelectionCallback: (Date) -> () = {_ in}
51 |
52 | // MARK: - Initializer
53 |
54 | /// Initializes the SSDatePickerManager with the provided configuration.
55 | ///
56 | /// - Parameters:
57 | /// - currentMonth: The initial current month to be displayed in the date picker.
58 | /// - selectedDate: The initially selected date to be displayed in the date picker.
59 | /// - configuration: The style configuration for the SSDatePicker.
60 | init(currentMonth: Date = Date(), selectedDate: Date? = nil, configuration: SSDatePickerConfiguration = SSDatePickerConfiguration()) {
61 | self.currentMonth = currentMonth
62 | self.selectedDate = selectedDate
63 | self.configuration = configuration
64 | self.lastCurrentMonth = currentMonth
65 | self.lastSelectedDate = selectedDate
66 | }
67 |
68 | // MARK: - Methods
69 |
70 | /// Advances to the next month, year based on the current view (date, month, or year).
71 | func actionNext(for view: SelectionView) {
72 | switch view {
73 | case .date:
74 | currentMonth = currentMonth.getNextMonth(calendar) ?? currentMonth
75 | case .month:
76 | currentMonth = currentMonth.getNextYear(calendar) ?? currentMonth
77 | updateYearSelection(date: currentMonth)
78 | case .year:
79 | guard let year = self.yearRange.last else { return }
80 | self.updateYearRange(year: year+12)
81 | }
82 | }
83 |
84 | /// Moves back to the previous month, year based on the current view (date, month, or year).
85 | func actionPrev(for view: SelectionView) {
86 | switch view {
87 | case .date:
88 | currentMonth = currentMonth.getPreviousMonth(calendar) ?? currentMonth
89 | case .month:
90 | currentMonth = currentMonth.getPreviousYear(calendar) ?? currentMonth
91 | updateYearSelection(date: currentMonth)
92 | case .year:
93 | guard let year = self.yearRange.first else { return }
94 | self.updateYearRange(year: year-1)
95 | }
96 | }
97 |
98 | /// Updates the year selection based on the chosen year.
99 | func updateYearSelection(date: Date) {
100 | updateYearSelection(year: date.year(calendar))
101 | }
102 |
103 | /// Checks if a given month is currently selected in the date picker.
104 | func isSelected(_ month: String) -> Bool {
105 | month.lowercased() == (selectedDate ?? currentMonth).fullMonth.lowercased()
106 | }
107 |
108 | /// Checks if a given year is currently selected in the date picker.
109 | func isSelected(_ year: Int) -> Bool {
110 | year == (selectedDate ?? currentMonth).year(calendar)
111 | }
112 |
113 | /// Updates the date selection based on the chosen date.
114 | func updateDateSelection(date: Date) {
115 | if configuration.allowMultipleSelection {
116 | if selectedDates == nil {
117 | self.selectedDates = []
118 | }
119 | updateMultipleSelection(date: date)
120 | } else if configuration.allowRangeSelection {
121 | self.updateRangeSelection(date: date)
122 | } else {
123 | self.selectedDate = date
124 | }
125 | }
126 |
127 | func updateMultipleSelection(date: Date) {
128 | // Find the index of the selected date in the array, if it exists
129 | if let existingIndex = selectedDates?.firstIndex(where: { selectedDate in
130 | calendar.isDate(date, equalTo: selectedDate, toGranularities: [.day, .month, .year])
131 | }) {
132 | // If the date is already selected, remove it to support deselection
133 | selectedDates?.remove(at: existingIndex)
134 | } else {
135 | // If the date is not selected, add it to the selected dates array
136 | selectedDates?.append(date)
137 | }
138 | }
139 |
140 | func handleMultiDateDeselection(date: Date) {
141 |
142 | }
143 |
144 | /// Updates the range selection based on the chosen date.
145 | func updateRangeSelection(date: Date) {
146 | if let startDate = startDate , date >= configuration.calendar.startOfDay(for: startDate) {
147 | endDate = date
148 | } else {
149 | startDate = date
150 | }
151 | }
152 |
153 | /// Updates the month selection based on the chosen month.
154 | func updateMonthSelection(month: Int) {
155 | guard let selectedDate else {
156 | var component = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: self.currentMonth)
157 | component.month = month
158 | self.currentMonth = calendar.date(from: component) ?? currentMonth
159 | return
160 | }
161 | var component = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: selectedDate)
162 | component.month = month
163 | self.selectedDate = calendar.date(from: component)
164 | self.currentMonth = self.selectedDate ?? currentMonth
165 | }
166 |
167 | /// Updates the year selection based on the chosen year.
168 | func updateYearSelection(year: Int) {
169 | guard let selectedDate else {
170 | var component = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: self.currentMonth)
171 | component.year = year
172 | self.currentMonth = calendar.date(from: component) ?? currentMonth
173 | return
174 | }
175 | var component = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: selectedDate)
176 | component.year = year
177 | self.selectedDate = calendar.date(from: component)
178 | self.currentMonth = self.selectedDate ?? currentMonth
179 | }
180 |
181 | /// Updates the displayed year range in the year selection view.
182 | func updateYearRange(year: Int) {
183 | let lowerBound = year - 11
184 | let upperBound = year
185 | self.yearRange = Array(lowerBound...upperBound)
186 | }
187 |
188 | /// Reverts the selection to the last known values before the user's interaction.
189 | ///
190 | /// - Parameter view: The view (date, month, or year) for which the selection is being canceled.
191 | func selectionCanceled(for view: SelectionView) {
192 | switch view {
193 | case .date:
194 | self.selectedDate = lastSelectedDate
195 | case .month, .year:
196 | self.currentMonth = lastCurrentMonth
197 | }
198 | }
199 |
200 | /// Confirms the selection and notifies the delegate.
201 | ///
202 | /// - Parameter view: The view (date, month, or year) for which the selection is being confirmed.
203 | func selectionConfirmed(for view: SelectionView) {
204 | switch view {
205 | case .date:
206 | lastSelectedDate = self.selectedDate
207 | handleCallback()
208 | case .month, .year:
209 | lastCurrentMonth = self.currentMonth
210 | }
211 | }
212 |
213 | /// Notifies the delegate about the selected dates based on the configuration.
214 | func handleCallback() {
215 | if configuration.allowMultipleSelection {
216 | guard let selectedDates else { return }
217 | multiDateSelectionCallback(selectedDates)
218 | } else if configuration.allowRangeSelection {
219 | guard let startDate, let endDate else { return }
220 | dateRangeSelectionCallback(DateRange(startDate, endDate))
221 | } else {
222 | guard let selectedDate else { return }
223 | dateSelectionCallback(selectedDate)
224 | }
225 | }
226 |
227 | /// Determines if a given date can be selected based on a list of disabled dates.
228 | /// - Parameters:
229 | /// - date: The date to be checked for selectability.
230 | /// - Returns: `true` if the date is selectable, `false` if it is disabled.
231 | func canSelectDate(_ date: Date) -> Bool {
232 | guard let disableDates = disableDates else {
233 | return true // all dates are selectable if disableDates is nil
234 | }
235 |
236 | return !disableDates.contains { disableDate in
237 | calendar.isDate(date, equalTo: disableDate, toGranularities: [.day, .month, .year])
238 | }
239 | }
240 |
241 | }
242 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | # SSDateTimePicker
7 |
8 | SSDateTimePicker is a SwiftUI library that simplifies date and time selection in your applications, providing a variety of features and a customizable UI for both date and time pickers.
9 |
10 |
11 |
12 |
13 | | Time Picker |
14 | Date Picker |
15 | Multiple Date Picker |
16 | Date Range Picker |
17 |
18 |
19 |
20 |
21 |
22 |
23 | |
24 |
25 |
26 | |
27 |
28 |
29 | |
30 |
31 |
32 | |
33 |
34 |
35 |
36 |
37 |
38 | ## Features!
39 | * Single Date Selection
40 | * Multiple Date Selection
41 | * Date Range Selection
42 | * Disable Past or Future Dates
43 | * Disable Specific Dates
44 | * Limit date selection to a predefined range
45 | * Time selection with a clock-style interface
46 | * Personalize fonts and colors for seamless integration with your app's design.
47 |
48 | ## Requirements
49 |
50 | * iOS 15.0+
51 | * Xcode 12+
52 |
53 | ## Installation
54 |
55 | ### [Swift Package Manager](https://swift.org/package-manager/)
56 |
57 | You can install `SSDateTimePicker` using Swift Package Manager by:
58 |
59 | 1. Go to `Xcode` -> `File` -> `Add Package Dependencies...`
60 | 2. Add package URL [https://github.com/SimformSolutionsPvtLtd/SSDateTimePicker][SSDateTimePicker]
61 |
62 | ### [CocoaPods](http://cocoapods.org)
63 |
64 | To install `SSDateTimePicker`, simply add the following line to your Podfile:
65 |
66 | ```swift
67 | pod 'SSDateTimePicker'
68 | ```
69 |
70 |
71 | ## Usage
72 |
73 | ### SSDatePicker
74 |
75 | SSDatePicker offers versatile date selection options, including single date, multiple date, and date range selection. Follow this guide to set up the date picker according to your preferences:
76 |
77 | 1. import `SSDateTimePicker`.
78 | 2. Add a bool to control date picker presentation state.
79 | 3. Add a `SSDatePicker` in your view.
80 |
81 |
82 | ```swift
83 | import SSDateTimePicker
84 |
85 | struct PickerExample: View {
86 |
87 | @State var showDatePicker: Bool = false
88 | @State var selectedDate: Date?
89 |
90 | var body: some View {
91 | ZStack {
92 | Text("Your view")
93 | SSDatePicker(showDatePicker: $showDatePicker)
94 | }
95 | }
96 | }
97 | ```
98 |
99 | ###### Single Date Selection Picker
100 |
101 | 1. Add `.selectedDate(selectedDate)` modifier to pre-select specific date.
102 | 2. Set the callback closure ` .onDateSelection` to get selected date.
103 |
104 | ```swift
105 | SSDatePicker(showDatePicker: $showDatePicker)
106 | .selectedDate(selectedDate)
107 | .onDateSelection({ date in
108 | selectedDate = date
109 | })
110 | ```
111 |
112 | ###### Multiple date selection picker
113 | 1. To enable multiple date selection add `.enableMultipleDateSelection()` modifier.
114 | 2. Add `.selectedDates(selectedDates)` modifier to pre-select specific dates.
115 | 4. Set `.onMultiDateSelection` callback to get selected multiple dates.
116 |
117 | ```swift
118 | SSDatePicker(showDatePicker: $showDatePicker)
119 | .enableMultipleDateSelection()
120 | .selectedDates(selectedDates)
121 | .onMultiDateSelection({ dates in
122 | selectedDates = dates
123 | })
124 | ```
125 |
126 | ###### Date Range Selection Picker
127 |
128 | 1. To enable date range selection add `.enableDateRangeSelection()` modifier.
129 | 2. Add `.selectedDateRange(selectedDateRange))` modifier to pre-select specific dates.
130 | 2. Sets `.onDateRangeSelection` callback to get selected date range.
131 |
132 | ```swift
133 | SSDatePicker(showDatePicker: $showDatePicker)
134 | .enableDateRangeSelection()
135 | .selectedDateRange(selectedDateRange)
136 | .onDateRangeSelection({ dateRange in
137 | selectedDateRange = dateRange
138 | })
139 | ```
140 |
141 | ### SSTimePicker
142 |
143 | 1. import `SSDateTimePicker`
144 | 2. Add a bool to control date-time picker presentation state
145 | 3. Add a `SSTimePicker` in your view
146 | 4. Add `.selectedTime(pickerViewModel.selectedTime)` modifier to pre-select specific dates.
147 | 2. Set `.onTimeSelection` callback to get selected date range.
148 |
149 | ```swift
150 | import SSDateTimePicker
151 |
152 | struct TimePickerExample: View {
153 |
154 | @State var showTimePicker: Bool = false
155 | @State var selectedTime: Time?
156 |
157 | var body: some View {
158 | ZStack {
159 | Text("Your view")
160 | SSTimePicker(showTimePicker: $showTimePicker)
161 | .selectedTime(selectedTime)
162 | .onTimeSelection { time in
163 | selectedTime = time
164 | }
165 | }
166 | }
167 | }
168 | ```
169 |
170 | ## Configuration Guide
171 |
172 | Explore the following modifiers to effortlessly customize the UI and behaviour of SSDateTimePicker to suit your preferences.
173 |
174 | ### SSDatePicker
175 |
176 | ###### Behavioral Modifiers
177 |
178 | - `.minimumDate(_ date: Date)` - Set the minimum selectable date in the date picker.
179 | - `.maximumDate(_ date: Date)` - Set the maximum selectable date in the date picker.
180 | - `.disableDates(_ dates: [Date])` - Block the selection of specific dates.
181 | - `.disablePastDate()` - Prevent the selection of past dates.
182 | - `.disableFutureDate()` - Prevent the selection of future dates.
183 | - `.currentMonth(_ date: Date)` - Set the initial display month in the date picker, providing a specific starting point when the picker is opened. By default it will open current month claendar.
184 | - `.enableDateRangeSelection()` - Enable the selection of date range.
185 | - `.enableMultipleDateSelection()` - Enable the selection of multiple dates.
186 | - `.selectedDate(_ date: Date?)` - Pre-select a specific date in the date picker.
187 | - `.selectedDates(_ dates: [Date]?)` - Pre-select a specific dates in the date picker.
188 | - `.selectedDates(_ dateRange: DateRange?)` - Pre-select a specific date range in the date picker.
189 | - `.calendar(_ calendar: Calendar)` - Set the calendar used by the date picker.
190 |
191 | ###### UI Modifiers
192 |
193 | - `.themeColor(pickerBackgroundColor: Color, primaryColor: Color)` - Define the overall theme color, where the primary color sets the background of selected dates, buttons and selectected month, year foreground.
194 | - `.todayColor(backgroundColor: Color?, foregroundColor: Color?)` - Highlight today's date with specific foreground and background colors.
195 | - `.todayDateSelectionColor(backgroundColor: Color?, foregroundColor: Color?)` - Adjust the foreground and background colors for the today's date selection state.
196 | - `.headerTitleStyle(color: Color?, font: Font?)` - Customize the font and color of the header text.
197 | - `.headerDateStyle(color: Color?, font: Font?)` - Customize font and color for header date text.
198 | - `.weekdayStyle(color: Color?, font: Font?)` - Adjust the text color and font of weekdays.
199 | - `.dateStyle(color: Color?, font: Font?)` - Customize color and font for date text.
200 | - `.monthStyle(color: Color?, font: Font?)` - Modify the font and color for the selected month.
201 | - `.selectedMonthStyle(color: Color?, font: Font?)` - Customize font and color for selected month.
202 | - `.yearStyle(color: Color?, font: Font?) -> SSDatePicker ` - Customize the text color and font for the year text.
203 | - `.selectedYearStyle(color: Color?, font: Font?)` - Adjust the font and color for the selected year.
204 | - `.buttonStyle(color: Color?, font: Font?)` - Set the font and color for the buttons.
205 | - `.currentMonthYearLabelStyle(color: Color?, font: Font?)` - Customize the color and font for the label displaying the current month and year in the bottom navigation.
206 | - `.selectedDateColor(backgroundColor: Color?, foregroundColor: Color?) ` - Change the foreground and background color for selected dates.
207 | - `.pickerBackgroundColor(_ color: Color) ` - Define the background color of the entire picker view.
208 | - `.sepratorLineColor(_ color: Color)` - Change the color of the separator line within the picker.
209 | - `.popupOverlayColor(_ color: Color)` - Customize the color of the popup overlay,
210 |
211 | ### SSTimePicker
212 |
213 | - `themeColor(pickerBackgroundColor: Color, primaryColor: Color, timeLabelBackgroundColor: Color)` - Apply a custom color scheme to the time picker, primary color is designated for the clock hand and the foreground of the time label.
214 | - `selectedTime(_ time: Time?)` - Set the initially selected time for the time picker.
215 | - `headerTitleStyle(color: Color?, font: Font?)` - Customize the style of the header title.
216 | - `timeLabelStyle(color: Color?, font: Font?)` - Customize time label(HH:MM) font and foreground color.
217 | - `timeFormatStyle(color: Color?, font: Font?)` - Modify Time format(AM/PM) color and font.
218 | - `selectedTimeFormatStyle(color: Color?, font: Font?)` - Customize selected time format(AM/PM) style.
219 | - `clockNumberStyle(color: Color?, font: Font?)` - Customize the style of the clock numbers.
220 | - `buttonStyle(color: Color?, font: Font?)` - Customize buttons font and foreground color.
221 |
222 | ## Find this samples useful? :heart:
223 |
224 | Support it by joining [stargazers] :star: for this repository.
225 |
226 | ## How to Contribute :handshake:
227 |
228 | Whether you're helping us fix bugs, improve the docs, or a feature request, we'd love to have you! :muscle: \
229 | Check out our __[Contributing Guide]__ for ideas on contributing.
230 |
231 | ## Bugs and Feedback
232 |
233 | For bugs, feature feature requests, and discussion use [GitHub Issues].
234 |
235 | ## Other Mobile Libraries
236 |
237 | Check out our other libraries [Awesome-Mobile-Libraries].
238 |
239 | ## License
240 |
241 | Distributed under the MIT license. See [LICENSE] for details.
242 |
243 |
244 |
245 |
246 | [SSDateTimePicker]: https://github.com/SimformSolutionsPvtLtd/SSDateTimePicker
247 |
248 | [Swift Package Manager]: https://www.swift.org/package-manager
249 |
250 | [stargazers]: https://github.com/SimformSolutionsPvtLtd/SSDateTimePicker/stargazers
251 |
252 | [Awesome-Mobile-Libraries]: https://github.com/SimformSolutionsPvtLtd/Awesome-Mobile-Libraries
253 |
254 | [license]: LICENSE
255 |
256 | [Github Issues]: https://github.com/SimformSolutionsPvtLtd/SSDateTimePicker/issues
257 |
258 | [Contributing Guide]: CONTRIBUTING.md
259 |
260 |
261 |
262 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Views/Time Picker/SSTimePicker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SSTimePicker.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 04/12/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public struct SSTimePicker: View, TimePickerConfigurationDirectAccess {
11 |
12 | // MARK: - Properties
13 |
14 | @ObservedObject var timePickerManager: SSTimePickerManager = SSTimePickerManager()
15 | @Binding var showTimePicker: Bool
16 | @State var isInEditMode: Bool = false
17 |
18 | var configuration: SSTimePickerConfiguration {
19 | get {
20 | timePickerManager.configuration
21 | } set {
22 | timePickerManager.configuration = newValue
23 | }
24 | }
25 |
26 | //MARK: - Initializer
27 |
28 | public init(showTimePicker: Binding) {
29 | self._showTimePicker = showTimePicker
30 | }
31 |
32 | //MARK: - Body
33 |
34 | public var body: some View {
35 | ZStack(alignment: .center) {
36 | if showTimePicker {
37 | popupOverlayColor
38 | .ignoresSafeArea()
39 | .onTapGesture {
40 | actionCancel()
41 | }
42 | timePickerSubview
43 | .background(pickerBackgroundColor)
44 | .cornerRadius(pickerViewRadius)
45 | .padding(.leading, SSPickerConstants.pickerLeadingTrailing)
46 | .padding(.trailing, SSPickerConstants.pickerLeadingTrailing)
47 | .compositingGroup()
48 | }
49 | }
50 | .onChange(of: showTimePicker) { newValue in
51 | if showTimePicker {
52 | timePickerManager.setUpTimeAndAngle()
53 | }
54 | }
55 | }
56 |
57 | //MARK: - Sub views
58 |
59 | private var timePickerSubview: some View {
60 | VStack(alignment: .leading, spacing: SSPickerConstants.verticleSpacingTen) {
61 | timePickerHeader
62 | SSClockPicker(timePickerManager: timePickerManager)
63 | bottomButtons
64 | }
65 | .padding(SSPickerConstants.pickerViewInnerPadding)
66 | }
67 |
68 | private var timePickerHeader: some View {
69 | VStack(alignment: .leading, spacing: SSPickerConstants.verticleSpacingTen) {
70 | lblSelectedDate
71 | Divider()
72 | }
73 | }
74 |
75 | private var lblSelectedDate: some View {
76 | VStack(alignment: .leading, spacing: SSPickerConstants.verticleSpacingTen) {
77 | Text(SSLocalizedString.selectTime)
78 | .font(headerTitleFont)
79 | .foregroundColor(headerTitleColor)
80 | textFieldHourMinutes
81 | }
82 | .padding(SSPickerConstants.paddingTen)
83 | }
84 |
85 | private var textFieldHourMinutes: some View {
86 | HStack(spacing: 4) {
87 | SSTimeTextField(time: $timePickerManager.hourSelected, configuration: configuration, isHourField: true, isInEditMode: $isInEditMode)
88 | .simultaneousGesture(TapGesture().onEnded {
89 | timePickerManager.actionShowHourClock()
90 | })
91 | Text(":")
92 | .font(timeLabelFont)
93 | .foregroundColor(timeLabelForegroundColor)
94 | SSTimeTextField(time: $timePickerManager.minutesSelected, configuration: configuration, isHourField: false, isInEditMode: $isInEditMode)
95 | .simultaneousGesture(TapGesture().onEnded {
96 | timePickerManager.actionShowMinuteClock()
97 | })
98 | amPMView
99 | .padding(2)
100 | Spacer()
101 | }
102 | .toolbar {
103 | ToolbarItem(placement: .keyboard) {
104 | HStack {
105 | Spacer()
106 | Button(SSLocalizedString.done) {
107 | actionDone()
108 | }
109 | }
110 | }
111 | }
112 | }
113 |
114 | private var amPMView: some View {
115 | VStack {
116 | btnAM
117 | btnPM
118 | }
119 | }
120 |
121 | private var btnAM: some View {
122 | Button {
123 | withAnimation {
124 | timePickerManager.selectedTimeFromat = .am
125 | }
126 | } label: {
127 | labelTimeFormat(SSLocalizedString.am, isSelected: timePickerManager.selectedTimeFromat == .am)
128 | }
129 | }
130 |
131 | private var btnPM: some View {
132 | Button {
133 | withAnimation {
134 | timePickerManager.selectedTimeFromat = .pm
135 | }
136 | } label: {
137 | labelTimeFormat(SSLocalizedString.pm, isSelected: timePickerManager.selectedTimeFromat == .pm)
138 | }
139 | }
140 |
141 | private var bottomButtons: some View {
142 | HStack(spacing: SSPickerConstants.bottomButtonHSpacing) {
143 | Spacer()
144 | btnCancel
145 | btnOk
146 | }
147 | }
148 |
149 | private var btnCancel: some View {
150 | Button {
151 | withAnimation {
152 | self.actionCancel()
153 | }
154 | } label: {
155 | Text(SSLocalizedString.cancel)
156 | .themeButton(buttonsForegroundColor, buttonFont)
157 | }
158 | }
159 |
160 | private var btnOk: some View {
161 | Button {
162 | withAnimation {
163 | self.actionOk()
164 | }
165 | } label: {
166 | Text(SSLocalizedString.ok)
167 | .themeButton(buttonsForegroundColor, buttonFont)
168 | }
169 | }
170 |
171 | private func labelTimeFormat(_ format: String, isSelected: Bool) -> some View {
172 | Text(format)
173 | .font(isSelected ? selectedTimeFormatFont : timeFormatFont)
174 | .foregroundColor(isSelected ? timeFormatSelectionColor : timeFormatColor)
175 | .transition(.scale)
176 | .animation(.easeIn, value: isSelected)
177 | }
178 |
179 | }
180 |
181 | extension SSTimePicker {
182 |
183 | //MARK: - Methods
184 |
185 | func actionCancel() {
186 | if timePickerManager.isMinuteClock {
187 | timePickerManager.isMinuteClock = false
188 | } else {
189 | showTimePicker.toggle()
190 | }
191 | }
192 |
193 | func actionOk() {
194 | timePickerManager.updateSelectedTime()
195 | showTimePicker.toggle()
196 | }
197 |
198 | func actionDone() {
199 | isInEditMode = false
200 | if timePickerManager.isMinuteClock {
201 | timePickerManager.updateCurrentMinuteAngle()
202 | } else {
203 | timePickerManager.updateCurrentHourAngle()
204 | }
205 | }
206 |
207 | }
208 |
209 | // MARK: - Modifiers
210 |
211 | extension SSTimePicker {
212 |
213 | /// Set the selected time for the time picker.
214 | /// - Parameter time: The selected time.
215 | /// - Returns: The modified SSTimePicker instance.
216 | public func selectedTime(_ time: Time?) -> SSTimePicker {
217 | let picker = self
218 | picker.timePickerManager.selectedTime = time
219 | return picker
220 | }
221 |
222 | /// Apply a custom theme to the time picker.
223 | /// - Parameters:
224 | /// - pickerBackgroundColor: Background color of the time picker.
225 | /// - primaryColor: Primary color used for various elements.
226 | /// - timeLabelBackgroundColor: Background color for the time label.
227 | /// - Returns: The modified SSTimePicker instance.
228 | public func themeColor(pickerBackgroundColor: Color, primaryColor: Color, timeLabelBackgroundColor: Color) -> SSTimePicker {
229 | var picker = self
230 | picker.configuration.timeLabelStyle.color = primaryColor
231 | picker.configuration.selectedTimeFormatStyle.color = primaryColor
232 | picker.configuration.buttonStyle.color = primaryColor
233 | picker.configuration.clockHandColor = primaryColor
234 | picker.configuration.pickerBackgroundColor = pickerBackgroundColor
235 | picker.configuration.timeLabelBackgroundColor = timeLabelBackgroundColor
236 | return picker
237 | }
238 |
239 | /// Customize the style of the header title.
240 | /// - Parameters:
241 | /// - color: Text color of the header title.
242 | /// - font: Font of the header title.
243 | /// - Returns: The modified SSTimePicker instance.
244 | public func headerTitleStyle(color: Color? = nil, font: Font? = nil) -> SSTimePicker {
245 | var picker = self
246 | color.map { picker.configuration.headerTitleStyle.color = $0 }
247 | font.map { picker.configuration.headerTitleStyle.font = $0 }
248 | return picker
249 | }
250 |
251 | /// Customize the style of the time label.
252 | /// - Parameters:
253 | /// - color: Text color of the time label.
254 | /// - font: Font of the time label.
255 | /// - Returns: The modified SSTimePicker instance.
256 | public func timeLabelStyle(color: Color? = nil, font: Font? = nil) -> SSTimePicker {
257 | var picker = self
258 | color.map { picker.configuration.timeLabelStyle.color = $0 }
259 | font.map { picker.configuration.timeLabelStyle.font = $0 }
260 | return picker
261 | }
262 |
263 | /// Customize the style of the time format(AM/PM).
264 | /// - Parameters:
265 | /// - color: Text color of the time format.
266 | /// - font: Font of the time format.
267 | /// - Returns: The modified SSTimePicker instance.
268 | public func timeFormatStyle(color: Color? = nil, font: Font? = nil) -> SSTimePicker {
269 | var picker = self
270 | color.map { picker.configuration.timeFormatStyle.color = $0 }
271 | font.map { picker.configuration.timeFormatStyle.font = $0 }
272 | return picker
273 | }
274 |
275 | /// Customize the style of the clock numbers.
276 | /// - Parameters:
277 | /// - color: Text color of the clock numbers.
278 | /// - font: Font of the clock numbers.
279 | /// - Returns: The modified SSTimePicker instance.
280 | public func clockNumberStyle(color: Color? = nil, font: Font? = nil) -> SSTimePicker {
281 | var picker = self
282 | color.map { picker.configuration.clockNumberStyle.color = $0 }
283 | font.map { picker.configuration.clockNumberStyle.font = $0 }
284 | return picker
285 | }
286 |
287 | /// Customize the style of the buttons.
288 | /// - Parameters:
289 | /// - color: Text color of the buttons.
290 | /// - font: Font of the buttons.
291 | /// - Returns: The modified SSTimePicker instance.
292 | public func buttonStyle(color: Color? = nil, font: Font? = nil) -> SSTimePicker {
293 | var picker = self
294 | color.map { picker.configuration.buttonStyle.color = $0 }
295 | font.map { picker.configuration.buttonStyle.font = $0 }
296 | return picker
297 | }
298 |
299 | /// Customize the style of the selected time format.
300 | /// - Parameters:
301 | /// - color: Text color of the selected time format.
302 | /// - font: Font of the selected time format.
303 | /// - Returns: The modified SSTimePicker instance.
304 | public func selectedTimeFormatStyle(color: Color? = nil, font: Font? = nil) -> SSTimePicker {
305 | var picker = self
306 | color.map { picker.configuration.selectedTimeFormatStyle.color = $0 }
307 | font.map { picker.configuration.selectedTimeFormatStyle.font = $0 }
308 | return picker
309 | }
310 |
311 | /// Set a callback closure to be executed when a time is selected.
312 | /// - Parameter completion: A closure to be called with the selected time.
313 | /// - Returns: The modified SSTimePicker instance.
314 | public func onTimeSelection(_ completion: @escaping (Date) -> ()) -> SSTimePicker {
315 | let picker = self
316 | picker.timePickerManager.timeSelectionCallback = completion
317 | return picker
318 | }
319 |
320 | }
321 |
322 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 650720D32B194D2100AC1FB6 /* PickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720D22B194D2100AC1FB6 /* PickerViewModel.swift */; };
11 | 6549D9AC2B2C4B4B0089EBB1 /* ThemeCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6549D9AB2B2C4B4B0089EBB1 /* ThemeCard.swift */; };
12 | 6549D9B02B2C4DBA0089EBB1 /* View+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6549D9AF2B2C4DBA0089EBB1 /* View+Extension.swift */; };
13 | 658A5CE12B3523B50044930D /* SSDateTimePicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658A5CDC2B3523490044930D /* SSDateTimePicker.framework */; };
14 | 658A5CE22B3523B50044930D /* SSDateTimePicker.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 658A5CDC2B3523490044930D /* SSDateTimePicker.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
15 | 65AB6B0B2B283131009EA7EC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 65AB6B0D2B283131009EA7EC /* Localizable.strings */; };
16 | 65AB6B102B2839F9009EA7EC /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AB6B0F2B2839F9009EA7EC /* Utils.swift */; };
17 | 65AB6B122B283AED009EA7EC /* LocalizedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AB6B112B283AED009EA7EC /* LocalizedString.swift */; };
18 | 65AB6B152B285309009EA7EC /* Color+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AB6B142B285309009EA7EC /* Color+Extension.swift */; };
19 | 65E0584E2B0E3E850049A7BA /* ExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E0584D2B0E3E850049A7BA /* ExampleApp.swift */; };
20 | 65E058502B0E3E850049A7BA /* PickerExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E0584F2B0E3E850049A7BA /* PickerExampleView.swift */; };
21 | 65E058522B0E3E860049A7BA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 65E058512B0E3E860049A7BA /* Assets.xcassets */; };
22 | 65E058552B0E3E860049A7BA /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 65E058542B0E3E860049A7BA /* Preview Assets.xcassets */; };
23 | /* End PBXBuildFile section */
24 |
25 | /* Begin PBXContainerItemProxy section */
26 | 658A5CDB2B3523490044930D /* PBXContainerItemProxy */ = {
27 | isa = PBXContainerItemProxy;
28 | containerPortal = 658A5CD62B3523490044930D /* SSDateTimePicker.xcodeproj */;
29 | proxyType = 2;
30 | remoteGlobalIDString = 65E0580B2B0E2B260049A7BA;
31 | remoteInfo = SSDateTimePicker;
32 | };
33 | 658A5CDD2B3523490044930D /* PBXContainerItemProxy */ = {
34 | isa = PBXContainerItemProxy;
35 | containerPortal = 658A5CD62B3523490044930D /* SSDateTimePicker.xcodeproj */;
36 | proxyType = 2;
37 | remoteGlobalIDString = 65E058152B0E2B260049A7BA;
38 | remoteInfo = SSDateTimePickerTests;
39 | };
40 | /* End PBXContainerItemProxy section */
41 |
42 | /* Begin PBXCopyFilesBuildPhase section */
43 | 6520E7C62B2D9BDF008D7329 /* Embed Frameworks */ = {
44 | isa = PBXCopyFilesBuildPhase;
45 | buildActionMask = 2147483647;
46 | dstPath = "";
47 | dstSubfolderSpec = 10;
48 | files = (
49 | 658A5CE22B3523B50044930D /* SSDateTimePicker.framework in Embed Frameworks */,
50 | );
51 | name = "Embed Frameworks";
52 | runOnlyForDeploymentPostprocessing = 0;
53 | };
54 | /* End PBXCopyFilesBuildPhase section */
55 |
56 | /* Begin PBXFileReference section */
57 | 650720D22B194D2100AC1FB6 /* PickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerViewModel.swift; sourceTree = ""; };
58 | 6549D9AB2B2C4B4B0089EBB1 /* ThemeCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeCard.swift; sourceTree = ""; };
59 | 6549D9AF2B2C4DBA0089EBB1 /* View+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Extension.swift"; sourceTree = ""; };
60 | 658A5CD62B3523490044930D /* SSDateTimePicker.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SSDateTimePicker.xcodeproj; path = ../Sources/SSDateTimePicker/SSDateTimePicker.xcodeproj; sourceTree = ""; };
61 | 65AB6B0C2B283131009EA7EC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; };
62 | 65AB6B0F2B2839F9009EA7EC /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; };
63 | 65AB6B112B283AED009EA7EC /* LocalizedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedString.swift; sourceTree = ""; };
64 | 65AB6B142B285309009EA7EC /* Color+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Extension.swift"; sourceTree = ""; };
65 | 65E0584A2B0E3E850049A7BA /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
66 | 65E0584D2B0E3E850049A7BA /* ExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleApp.swift; sourceTree = ""; };
67 | 65E0584F2B0E3E850049A7BA /* PickerExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerExampleView.swift; sourceTree = ""; };
68 | 65E058512B0E3E860049A7BA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
69 | 65E058542B0E3E860049A7BA /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
70 | /* End PBXFileReference section */
71 |
72 | /* Begin PBXFrameworksBuildPhase section */
73 | 65E058472B0E3E850049A7BA /* Frameworks */ = {
74 | isa = PBXFrameworksBuildPhase;
75 | buildActionMask = 2147483647;
76 | files = (
77 | 658A5CE12B3523B50044930D /* SSDateTimePicker.framework in Frameworks */,
78 | );
79 | runOnlyForDeploymentPostprocessing = 0;
80 | };
81 | /* End PBXFrameworksBuildPhase section */
82 |
83 | /* Begin PBXGroup section */
84 | 650720CF2B194C7900AC1FB6 /* ViewModel */ = {
85 | isa = PBXGroup;
86 | children = (
87 | 650720D22B194D2100AC1FB6 /* PickerViewModel.swift */,
88 | );
89 | path = ViewModel;
90 | sourceTree = "";
91 | };
92 | 658A5CD72B3523490044930D /* Products */ = {
93 | isa = PBXGroup;
94 | children = (
95 | 658A5CDC2B3523490044930D /* SSDateTimePicker.framework */,
96 | 658A5CDE2B3523490044930D /* SSDateTimePickerTests.xctest */,
97 | );
98 | name = Products;
99 | sourceTree = "";
100 | };
101 | 658A5CDF2B3523590044930D /* Frameworks */ = {
102 | isa = PBXGroup;
103 | children = (
104 | );
105 | name = Frameworks;
106 | sourceTree = "";
107 | };
108 | 65AB6B0E2B28393B009EA7EC /* View */ = {
109 | isa = PBXGroup;
110 | children = (
111 | 65E0584F2B0E3E850049A7BA /* PickerExampleView.swift */,
112 | );
113 | path = View;
114 | sourceTree = "";
115 | };
116 | 65AB6B132B283AF2009EA7EC /* Common */ = {
117 | isa = PBXGroup;
118 | children = (
119 | 65AB6B0F2B2839F9009EA7EC /* Utils.swift */,
120 | 65AB6B112B283AED009EA7EC /* LocalizedString.swift */,
121 | 6549D9AB2B2C4B4B0089EBB1 /* ThemeCard.swift */,
122 | );
123 | path = Common;
124 | sourceTree = "";
125 | };
126 | 65AB6B162B285312009EA7EC /* Extension */ = {
127 | isa = PBXGroup;
128 | children = (
129 | 65AB6B142B285309009EA7EC /* Color+Extension.swift */,
130 | 6549D9AF2B2C4DBA0089EBB1 /* View+Extension.swift */,
131 | );
132 | path = Extension;
133 | sourceTree = "";
134 | };
135 | 65E058412B0E3E850049A7BA = {
136 | isa = PBXGroup;
137 | children = (
138 | 658A5CD62B3523490044930D /* SSDateTimePicker.xcodeproj */,
139 | 65AB6B0D2B283131009EA7EC /* Localizable.strings */,
140 | 65E0584C2B0E3E850049A7BA /* Example */,
141 | 65E0584B2B0E3E850049A7BA /* Products */,
142 | 658A5CDF2B3523590044930D /* Frameworks */,
143 | );
144 | sourceTree = "";
145 | };
146 | 65E0584B2B0E3E850049A7BA /* Products */ = {
147 | isa = PBXGroup;
148 | children = (
149 | 65E0584A2B0E3E850049A7BA /* Example.app */,
150 | );
151 | name = Products;
152 | sourceTree = "";
153 | };
154 | 65E0584C2B0E3E850049A7BA /* Example */ = {
155 | isa = PBXGroup;
156 | children = (
157 | 65AB6B162B285312009EA7EC /* Extension */,
158 | 65AB6B132B283AF2009EA7EC /* Common */,
159 | 65AB6B0E2B28393B009EA7EC /* View */,
160 | 650720CF2B194C7900AC1FB6 /* ViewModel */,
161 | 65E0584D2B0E3E850049A7BA /* ExampleApp.swift */,
162 | 65E058512B0E3E860049A7BA /* Assets.xcassets */,
163 | 65E058532B0E3E860049A7BA /* Preview Content */,
164 | );
165 | path = Example;
166 | sourceTree = "";
167 | };
168 | 65E058532B0E3E860049A7BA /* Preview Content */ = {
169 | isa = PBXGroup;
170 | children = (
171 | 65E058542B0E3E860049A7BA /* Preview Assets.xcassets */,
172 | );
173 | path = "Preview Content";
174 | sourceTree = "";
175 | };
176 | /* End PBXGroup section */
177 |
178 | /* Begin PBXNativeTarget section */
179 | 65E058492B0E3E850049A7BA /* Example */ = {
180 | isa = PBXNativeTarget;
181 | buildConfigurationList = 65E058582B0E3E860049A7BA /* Build configuration list for PBXNativeTarget "Example" */;
182 | buildPhases = (
183 | 65E058462B0E3E850049A7BA /* Sources */,
184 | 65E058472B0E3E850049A7BA /* Frameworks */,
185 | 65E058482B0E3E850049A7BA /* Resources */,
186 | 6520E7C62B2D9BDF008D7329 /* Embed Frameworks */,
187 | );
188 | buildRules = (
189 | );
190 | dependencies = (
191 | );
192 | name = Example;
193 | productName = Example;
194 | productReference = 65E0584A2B0E3E850049A7BA /* Example.app */;
195 | productType = "com.apple.product-type.application";
196 | };
197 | /* End PBXNativeTarget section */
198 |
199 | /* Begin PBXProject section */
200 | 65E058422B0E3E850049A7BA /* Project object */ = {
201 | isa = PBXProject;
202 | attributes = {
203 | BuildIndependentTargetsInParallel = 1;
204 | LastSwiftUpdateCheck = 1430;
205 | LastUpgradeCheck = 1430;
206 | TargetAttributes = {
207 | 65E058492B0E3E850049A7BA = {
208 | CreatedOnToolsVersion = 14.3.1;
209 | };
210 | };
211 | };
212 | buildConfigurationList = 65E058452B0E3E850049A7BA /* Build configuration list for PBXProject "Example" */;
213 | compatibilityVersion = "Xcode 14.0";
214 | developmentRegion = en;
215 | hasScannedForEncodings = 0;
216 | knownRegions = (
217 | en,
218 | Base,
219 | ar,
220 | );
221 | mainGroup = 65E058412B0E3E850049A7BA;
222 | productRefGroup = 65E0584B2B0E3E850049A7BA /* Products */;
223 | projectDirPath = "";
224 | projectReferences = (
225 | {
226 | ProductGroup = 658A5CD72B3523490044930D /* Products */;
227 | ProjectRef = 658A5CD62B3523490044930D /* SSDateTimePicker.xcodeproj */;
228 | },
229 | );
230 | projectRoot = "";
231 | targets = (
232 | 65E058492B0E3E850049A7BA /* Example */,
233 | );
234 | };
235 | /* End PBXProject section */
236 |
237 | /* Begin PBXReferenceProxy section */
238 | 658A5CDC2B3523490044930D /* SSDateTimePicker.framework */ = {
239 | isa = PBXReferenceProxy;
240 | fileType = wrapper.framework;
241 | path = SSDateTimePicker.framework;
242 | remoteRef = 658A5CDB2B3523490044930D /* PBXContainerItemProxy */;
243 | sourceTree = BUILT_PRODUCTS_DIR;
244 | };
245 | 658A5CDE2B3523490044930D /* SSDateTimePickerTests.xctest */ = {
246 | isa = PBXReferenceProxy;
247 | fileType = wrapper.cfbundle;
248 | path = SSDateTimePickerTests.xctest;
249 | remoteRef = 658A5CDD2B3523490044930D /* PBXContainerItemProxy */;
250 | sourceTree = BUILT_PRODUCTS_DIR;
251 | };
252 | /* End PBXReferenceProxy section */
253 |
254 | /* Begin PBXResourcesBuildPhase section */
255 | 65E058482B0E3E850049A7BA /* Resources */ = {
256 | isa = PBXResourcesBuildPhase;
257 | buildActionMask = 2147483647;
258 | files = (
259 | 65E058552B0E3E860049A7BA /* Preview Assets.xcassets in Resources */,
260 | 65AB6B0B2B283131009EA7EC /* Localizable.strings in Resources */,
261 | 65E058522B0E3E860049A7BA /* Assets.xcassets in Resources */,
262 | );
263 | runOnlyForDeploymentPostprocessing = 0;
264 | };
265 | /* End PBXResourcesBuildPhase section */
266 |
267 | /* Begin PBXSourcesBuildPhase section */
268 | 65E058462B0E3E850049A7BA /* Sources */ = {
269 | isa = PBXSourcesBuildPhase;
270 | buildActionMask = 2147483647;
271 | files = (
272 | 65AB6B122B283AED009EA7EC /* LocalizedString.swift in Sources */,
273 | 65AB6B152B285309009EA7EC /* Color+Extension.swift in Sources */,
274 | 6549D9B02B2C4DBA0089EBB1 /* View+Extension.swift in Sources */,
275 | 6549D9AC2B2C4B4B0089EBB1 /* ThemeCard.swift in Sources */,
276 | 650720D32B194D2100AC1FB6 /* PickerViewModel.swift in Sources */,
277 | 65E058502B0E3E850049A7BA /* PickerExampleView.swift in Sources */,
278 | 65E0584E2B0E3E850049A7BA /* ExampleApp.swift in Sources */,
279 | 65AB6B102B2839F9009EA7EC /* Utils.swift in Sources */,
280 | );
281 | runOnlyForDeploymentPostprocessing = 0;
282 | };
283 | /* End PBXSourcesBuildPhase section */
284 |
285 | /* Begin PBXVariantGroup section */
286 | 65AB6B0D2B283131009EA7EC /* Localizable.strings */ = {
287 | isa = PBXVariantGroup;
288 | children = (
289 | 65AB6B0C2B283131009EA7EC /* en */,
290 | );
291 | name = Localizable.strings;
292 | sourceTree = "";
293 | };
294 | /* End PBXVariantGroup section */
295 |
296 | /* Begin XCBuildConfiguration section */
297 | 65E058562B0E3E860049A7BA /* Debug */ = {
298 | isa = XCBuildConfiguration;
299 | buildSettings = {
300 | ALWAYS_SEARCH_USER_PATHS = NO;
301 | CLANG_ANALYZER_NONNULL = YES;
302 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
303 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
304 | CLANG_ENABLE_MODULES = YES;
305 | CLANG_ENABLE_OBJC_ARC = YES;
306 | CLANG_ENABLE_OBJC_WEAK = YES;
307 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
308 | CLANG_WARN_BOOL_CONVERSION = YES;
309 | CLANG_WARN_COMMA = YES;
310 | CLANG_WARN_CONSTANT_CONVERSION = YES;
311 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
312 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
313 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
314 | CLANG_WARN_EMPTY_BODY = YES;
315 | CLANG_WARN_ENUM_CONVERSION = YES;
316 | CLANG_WARN_INFINITE_RECURSION = YES;
317 | CLANG_WARN_INT_CONVERSION = YES;
318 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
319 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
320 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
321 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
322 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
323 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
324 | CLANG_WARN_STRICT_PROTOTYPES = YES;
325 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
326 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
327 | CLANG_WARN_UNREACHABLE_CODE = YES;
328 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
329 | COPY_PHASE_STRIP = NO;
330 | DEBUG_INFORMATION_FORMAT = dwarf;
331 | ENABLE_STRICT_OBJC_MSGSEND = YES;
332 | ENABLE_TESTABILITY = YES;
333 | GCC_C_LANGUAGE_STANDARD = gnu11;
334 | GCC_DYNAMIC_NO_PIC = NO;
335 | GCC_NO_COMMON_BLOCKS = YES;
336 | GCC_OPTIMIZATION_LEVEL = 0;
337 | GCC_PREPROCESSOR_DEFINITIONS = (
338 | "DEBUG=1",
339 | "$(inherited)",
340 | );
341 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
342 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
343 | GCC_WARN_UNDECLARED_SELECTOR = YES;
344 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
345 | GCC_WARN_UNUSED_FUNCTION = YES;
346 | GCC_WARN_UNUSED_VARIABLE = YES;
347 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
348 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
349 | MTL_FAST_MATH = YES;
350 | ONLY_ACTIVE_ARCH = YES;
351 | SDKROOT = iphoneos;
352 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
353 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
354 | };
355 | name = Debug;
356 | };
357 | 65E058572B0E3E860049A7BA /* Release */ = {
358 | isa = XCBuildConfiguration;
359 | buildSettings = {
360 | ALWAYS_SEARCH_USER_PATHS = NO;
361 | CLANG_ANALYZER_NONNULL = YES;
362 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
363 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
364 | CLANG_ENABLE_MODULES = YES;
365 | CLANG_ENABLE_OBJC_ARC = YES;
366 | CLANG_ENABLE_OBJC_WEAK = YES;
367 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
368 | CLANG_WARN_BOOL_CONVERSION = YES;
369 | CLANG_WARN_COMMA = YES;
370 | CLANG_WARN_CONSTANT_CONVERSION = YES;
371 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
372 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
373 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
374 | CLANG_WARN_EMPTY_BODY = YES;
375 | CLANG_WARN_ENUM_CONVERSION = YES;
376 | CLANG_WARN_INFINITE_RECURSION = YES;
377 | CLANG_WARN_INT_CONVERSION = YES;
378 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
379 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
380 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
381 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
382 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
383 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
384 | CLANG_WARN_STRICT_PROTOTYPES = YES;
385 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
386 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
387 | CLANG_WARN_UNREACHABLE_CODE = YES;
388 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
389 | COPY_PHASE_STRIP = NO;
390 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
391 | ENABLE_NS_ASSERTIONS = NO;
392 | ENABLE_STRICT_OBJC_MSGSEND = YES;
393 | GCC_C_LANGUAGE_STANDARD = gnu11;
394 | GCC_NO_COMMON_BLOCKS = YES;
395 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
396 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
397 | GCC_WARN_UNDECLARED_SELECTOR = YES;
398 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
399 | GCC_WARN_UNUSED_FUNCTION = YES;
400 | GCC_WARN_UNUSED_VARIABLE = YES;
401 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
402 | MTL_ENABLE_DEBUG_INFO = NO;
403 | MTL_FAST_MATH = YES;
404 | SDKROOT = iphoneos;
405 | SWIFT_COMPILATION_MODE = wholemodule;
406 | SWIFT_OPTIMIZATION_LEVEL = "-O";
407 | VALIDATE_PRODUCT = YES;
408 | };
409 | name = Release;
410 | };
411 | 65E058592B0E3E860049A7BA /* Debug */ = {
412 | isa = XCBuildConfiguration;
413 | buildSettings = {
414 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
415 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
416 | CODE_SIGN_STYLE = Automatic;
417 | CURRENT_PROJECT_VERSION = 1;
418 | DEVELOPMENT_ASSET_PATHS = "\"Example/Preview Content\"";
419 | DEVELOPMENT_TEAM = K7XJG666ZW;
420 | ENABLE_PREVIEWS = YES;
421 | GENERATE_INFOPLIST_FILE = YES;
422 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
423 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
424 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
425 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
426 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
427 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
428 | LD_RUNPATH_SEARCH_PATHS = (
429 | "$(inherited)",
430 | "@executable_path/Frameworks",
431 | );
432 | MARKETING_VERSION = 1.0;
433 | PRODUCT_BUNDLE_IDENTIFIER = simform.Example;
434 | PRODUCT_NAME = "$(TARGET_NAME)";
435 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
436 | SUPPORTS_MACCATALYST = NO;
437 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
438 | SWIFT_EMIT_LOC_STRINGS = YES;
439 | SWIFT_VERSION = 5.0;
440 | TARGETED_DEVICE_FAMILY = 1;
441 | };
442 | name = Debug;
443 | };
444 | 65E0585A2B0E3E860049A7BA /* Release */ = {
445 | isa = XCBuildConfiguration;
446 | buildSettings = {
447 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
448 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
449 | CODE_SIGN_STYLE = Automatic;
450 | CURRENT_PROJECT_VERSION = 1;
451 | DEVELOPMENT_ASSET_PATHS = "\"Example/Preview Content\"";
452 | DEVELOPMENT_TEAM = K7XJG666ZW;
453 | ENABLE_PREVIEWS = YES;
454 | GENERATE_INFOPLIST_FILE = YES;
455 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
456 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
457 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
458 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
459 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
460 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
461 | LD_RUNPATH_SEARCH_PATHS = (
462 | "$(inherited)",
463 | "@executable_path/Frameworks",
464 | );
465 | MARKETING_VERSION = 1.0;
466 | PRODUCT_BUNDLE_IDENTIFIER = simform.Example;
467 | PRODUCT_NAME = "$(TARGET_NAME)";
468 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
469 | SUPPORTS_MACCATALYST = NO;
470 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
471 | SWIFT_EMIT_LOC_STRINGS = YES;
472 | SWIFT_VERSION = 5.0;
473 | TARGETED_DEVICE_FAMILY = 1;
474 | };
475 | name = Release;
476 | };
477 | /* End XCBuildConfiguration section */
478 |
479 | /* Begin XCConfigurationList section */
480 | 65E058452B0E3E850049A7BA /* Build configuration list for PBXProject "Example" */ = {
481 | isa = XCConfigurationList;
482 | buildConfigurations = (
483 | 65E058562B0E3E860049A7BA /* Debug */,
484 | 65E058572B0E3E860049A7BA /* Release */,
485 | );
486 | defaultConfigurationIsVisible = 0;
487 | defaultConfigurationName = Release;
488 | };
489 | 65E058582B0E3E860049A7BA /* Build configuration list for PBXNativeTarget "Example" */ = {
490 | isa = XCConfigurationList;
491 | buildConfigurations = (
492 | 65E058592B0E3E860049A7BA /* Debug */,
493 | 65E0585A2B0E3E860049A7BA /* Release */,
494 | );
495 | defaultConfigurationIsVisible = 0;
496 | defaultConfigurationName = Release;
497 | };
498 | /* End XCConfigurationList section */
499 | };
500 | rootObject = 65E058422B0E3E850049A7BA /* Project object */;
501 | }
502 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker/Views/Date picker/SSDatePicker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SSDatePicker.swift
3 | // DateTimePicker
4 | //
5 | // Created by Rizwana Desai on 24/11/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public struct SSDatePicker: View, DatePickerConfigurationDirectAccess {
11 |
12 | //MARK: - Properties
13 |
14 | @Binding var showDatePicker: Bool
15 | @State var currentView: SelectionView = .date
16 | @ObservedObject var datePickerManager: SSDatePickerManager = SSDatePickerManager()
17 |
18 | var configuration: SSDatePickerConfiguration {
19 | get {
20 | datePickerManager.configuration
21 | }
22 | set {
23 | datePickerManager.configuration = newValue
24 | }
25 | }
26 |
27 | private var weeks: [Date] {
28 | guard let monthInterval = calendar.dateInterval(of: .month, for: datePickerManager.currentMonth) else {
29 | return []
30 | }
31 | return calendar.generateDates(
32 | inside: monthInterval,
33 | matching: calendar.firstDayOfEveryWeek)
34 | }
35 |
36 | //MARK: - Initializer
37 |
38 | public init(showDatePicker: Binding) {
39 | self._showDatePicker = showDatePicker
40 | }
41 |
42 | //MARK: - Sub views
43 |
44 | public var body: some View {
45 | ZStack(alignment: .center) {
46 | if showDatePicker {
47 | popupOverlayColor
48 | .ignoresSafeArea()
49 | .onTapGesture {
50 | actionCancel()
51 | }
52 | calenderContainerView
53 | .background(pickerBackgroundColor)
54 | .cornerRadius(pickerViewRadius)
55 | .padding(.leading, SSPickerConstants.pickerLeadingTrailing)
56 | .padding(.trailing, SSPickerConstants.pickerLeadingTrailing)
57 | .compositingGroup()
58 | }
59 | }
60 | .environmentObject(datePickerManager)
61 | }
62 |
63 | private var pickerContainerView: some View {
64 | ZStack(alignment: .center) {
65 | switch currentView {
66 | case .date:
67 | dateSectionView
68 | case .month:
69 | SSMonthSelectionView()
70 | case .year:
71 | SSYearSelectionView(currentView: $currentView)
72 | }
73 | }
74 | }
75 |
76 | private var calenderContainerView: some View {
77 | VStack(alignment: .leading, spacing: SSPickerConstants.verticleSpacingTen) {
78 | datePickerHeader
79 | pickerContainerView
80 | calenderFooterView
81 | bottomButtons
82 | }
83 | .padding(SSPickerConstants.pickerViewInnerPadding)
84 | }
85 |
86 | private var datePickerHeader: some View {
87 | VStack(alignment: .leading, spacing: SSPickerConstants.paddingFive) {
88 | lblSelectedDate
89 | Divider()
90 | .background(sepratorLineColor)
91 | .padding(.bottom, SSPickerConstants.deviderBottomPadding)
92 | }
93 | }
94 |
95 | private var dateSectionView: some View {
96 | VStack(alignment: .leading, spacing: SSPickerConstants.verticleSpacingTen) {
97 | daysOfWeekView
98 | datesView
99 | }
100 | }
101 |
102 | private var lblSelectedDate: some View {
103 | VStack(alignment: .leading, spacing: SSPickerConstants.verticleSpacingTen) {
104 | Text(SSLocalizedString.selectDate)
105 | .font(headerTitleFont)
106 | .foregroundColor(headerTitleColor)
107 | Text(datePickerManager.selectedDate?.formatedString(headerDateFormat) ?? datePickerManager.currentMonth.monthYear)
108 | .font(headerDateFont)
109 | .foregroundColor(headerDateColor)
110 | }
111 | .padding(SSPickerConstants.paddingTen)
112 | }
113 |
114 | private var daysOfWeekView: some View {
115 | HStack(spacing: SSPickerConstants.horizontalSpacingDates) {
116 | ForEach(calendar.shortWeekdaySymbols, id: \.self) { dayOfWeek in
117 | Text(dayOfWeek.prefix(1))
118 | .font(weekdayTextFont)
119 | .frame(width: SSPickerConstants.widthForDaysOfWeek)
120 | .foregroundColor(weekdayTextColor)
121 | }
122 | }
123 | }
124 |
125 | private var datesView: some View {
126 | VStack(spacing: SSPickerConstants.verticleSpacingDates) {
127 | ForEach(weeks, id: \.self) { week in
128 | SSWeekDatesView(week: week)
129 | }
130 | }
131 | }
132 |
133 | private var calenderFooterView: some View {
134 | HStack {
135 | btnPrevious
136 | Spacer()
137 | lblMonthYear
138 | Spacer()
139 | btnNext
140 | }
141 | .frame(maxWidth: .infinity)
142 | .padding(SSPickerConstants.paddingFive)
143 | }
144 |
145 | private var lblMonthYear: some View {
146 | Button {
147 | withAnimation {
148 | updateView()
149 | }
150 | } label: {
151 | Text(currentMonthYear)
152 | .font(currentMonthYearBottomLabelFont)
153 | .foregroundColor(monthYearNavigationLabelColor)
154 | }
155 | }
156 |
157 | private var currentMonthYear: String {
158 | switch currentView {
159 | case .date:
160 | return datePickerManager.currentMonth.monthYear
161 | case .month:
162 | return String(datePickerManager.currentMonth.year(calendar))
163 | case .year:
164 | guard let startingYear = datePickerManager.yearRange.first, let endYear = datePickerManager.yearRange.last else {
165 | return String(datePickerManager.currentMonth.year(calendar))
166 | }
167 | return "\(startingYear) - \(endYear)"
168 | }
169 | }
170 |
171 | private var btnPrevious: some View {
172 | Button {
173 | self.datePickerManager.actionPrev(for: currentView)
174 | } label: {
175 | self.imageNextPrev(SSImageConstant.chevronLeft)
176 | }
177 | }
178 |
179 | private var btnNext: some View {
180 | Button {
181 | self.datePickerManager.actionNext(for: currentView)
182 | } label: {
183 | self.imageNextPrev(SSImageConstant.chevronRight)
184 | }
185 | }
186 |
187 | private func imageNextPrev(_ name: String) -> some View {
188 | Image(systemName: name)
189 | .foregroundColor(buttonsForegroundColor)
190 | .padding(SSPickerConstants.paddingFive)
191 | }
192 |
193 | private var bottomButtons: some View {
194 | HStack(spacing: SSPickerConstants.bottomButtonHSpacing) {
195 | Spacer()
196 | btnCancel
197 | btnOk
198 | }
199 | }
200 |
201 | private var btnCancel: some View {
202 | Button {
203 | withAnimation {
204 | self.actionCancel()
205 | }
206 | } label: {
207 | Text(SSLocalizedString.cancel)
208 | .themeButton(buttonsForegroundColor, buttonsFont)
209 | }
210 | }
211 |
212 | private var btnOk: some View {
213 | Button {
214 | withAnimation {
215 | self.actionOk()
216 | }
217 | } label: {
218 | Text(SSLocalizedString.ok)
219 | .themeButton(buttonsForegroundColor, buttonsFont)
220 | }
221 | }
222 |
223 | private func updateView() {
224 | switch currentView {
225 | case .date:
226 | currentView = .month
227 | case .month:
228 | currentView = .year
229 | case .year:
230 | currentView = .date
231 | }
232 | }
233 |
234 | }
235 |
236 | extension SSDatePicker {
237 |
238 | //MARK: - Action methods
239 |
240 | private func actionCancel() {
241 | datePickerManager.selectionCanceled(for: currentView)
242 | switch currentView {
243 | case .date:
244 | showDatePicker = false
245 | currentView = .date
246 | case .month:
247 | currentView = .date
248 | case .year:
249 | currentView = .month
250 | }
251 | }
252 |
253 | private func actionOk() {
254 | self.datePickerManager.selectionConfirmed(for: currentView)
255 | switch currentView {
256 | case .year, .month:
257 | currentView = .date
258 | case .date:
259 | showDatePicker = false
260 | currentView = .date
261 | }
262 | }
263 |
264 | }
265 |
266 | // MARK: - Modifiers
267 |
268 | extension SSDatePicker {
269 |
270 | /// Creates a custom-themed date picker.
271 | /// - Parameters:
272 | /// - pickerBackgroundColor: The background color of the picker view. Default is light pink.
273 | /// - primaryColor: The color for both the selected date's background and the buttons. Deafult is dark pink.
274 | /// - Returns: An updated SSDatePicker instance.
275 | public func themeColor(pickerBackgroundColor: Color = Color.lightPink, primaryColor: Color = Color.darkPink) -> SSDatePicker {
276 | var picker = self
277 | picker.configuration.selectedDateColor.backgroundColor = primaryColor
278 | picker.configuration.buttonStyle.color = primaryColor
279 | picker.configuration.pickerBackgroundColor = pickerBackgroundColor
280 | return picker
281 | }
282 |
283 | /// Sets the style for the header title in the date picker. 'Select Date'
284 | /// - Parameters:
285 | /// - color: The color of the header title.
286 | /// - font: The font of the header title.
287 | /// - Returns: An updated SSDatePicker instance.
288 | public func headerTitleStyle(color: Color? = nil, font: Font? = nil) -> SSDatePicker {
289 | var picker = self
290 | color.map { picker.configuration.headerTitleStyle.color = $0 }
291 | font.map { picker.configuration.headerTitleStyle.font = $0 }
292 | return picker
293 | }
294 |
295 | /// Sets the style for the header date in the date picker.
296 | /// - Parameters:
297 | /// - color: The color of the header date.
298 | /// - font: The font of the header date.
299 | /// - Returns: An updated SSDatePicker instance.
300 | public func headerDateStyle(color: Color? = nil, font: Font? = nil) -> SSDatePicker {
301 | var picker = self
302 | color.map { picker.configuration.headerDateStyle.color = $0 }
303 | font.map { picker.configuration.headerDateStyle.font = $0 }
304 | return picker
305 | }
306 |
307 | /// Sets the style for the weekday labels in the date picker.
308 | /// - Parameters:
309 | /// - color: The color of the weekday labels.
310 | /// - font: The font of the weekday labels.
311 | /// - Returns: An updated SSDatePicker instance.
312 | public func weekdayStyle(color: Color? = nil, font: Font? = nil) -> SSDatePicker {
313 | var picker = self
314 | color.map { picker.configuration.weekdayStyle.color = $0 }
315 | font.map { picker.configuration.weekdayStyle.font = $0 }
316 | return picker
317 | }
318 |
319 | /// Sets the style for the date labels in the date picker.
320 | /// - Parameters:
321 | /// - color: The color of the date labels.
322 | /// - font: The font of the date labels.
323 | /// - Returns: An updated SSDatePicker instance.
324 | public func dateStyle(color: Color? = nil, font: Font? = nil) -> SSDatePicker {
325 | var picker = self
326 | color.map { picker.configuration.dateStyle.color = $0 }
327 | font.map { picker.configuration.dateStyle.font = $0 }
328 | return picker
329 | }
330 |
331 | /// Sets the style for the month labels in the date picker.
332 | /// - Parameters:
333 | /// - color: The color of the month labels.
334 | /// - font: The font of the month labels.
335 | /// - Returns: An updated SSDatePicker instance.
336 | public func monthStyle(color: Color? = nil, font: Font? = nil) -> SSDatePicker {
337 | var picker = self
338 | color.map { picker.configuration.monthStyle.color = $0 }
339 | font.map { picker.configuration.monthStyle.font = $0 }
340 | return picker
341 | }
342 |
343 | /// Sets the style for the selected month in the date picker.
344 | /// - Parameters:
345 | /// - color: The color of the selected month.
346 | /// - font: The font of the selected month.
347 | /// - Returns: An updated SSDatePicker instance.
348 | public func selectedMonthStyle(color: Color? = nil, font: Font? = nil) -> SSDatePicker {
349 | var picker = self
350 | color.map { picker.configuration.selectedMonthStyle.color = $0 }
351 | font.map { picker.configuration.selectedMonthStyle.font = $0 }
352 | return picker
353 | }
354 |
355 | /// Sets the style for the year labels in the date picker.
356 | /// - Parameters:
357 | /// - color: The color of the year labels.
358 | /// - font: The font of the year labels.
359 | /// - Returns: An updated SSDatePicker instance.
360 | public func yearStyle(color: Color? = nil, font: Font? = nil) -> SSDatePicker {
361 | var picker = self
362 | color.map { picker.configuration.yearStyle.color = $0 }
363 | font.map { picker.configuration.yearStyle.font = $0 }
364 | return picker
365 | }
366 |
367 | /// Sets the style for the selected year in the date picker.
368 | /// - Parameters:
369 | /// - color: The color of the selected year.
370 | /// - font: The font of the selected year.
371 | /// - Returns: An updated SSDatePicker instance.
372 | public func selectedYearStyle(color: Color? = nil, font: Font? = nil) -> SSDatePicker {
373 | var picker = self
374 | color.map { picker.configuration.selectedYearStyle.color = $0 }
375 | font.map { picker.configuration.selectedYearStyle.font = $0 }
376 | return picker
377 | }
378 |
379 | /// Sets the style for the button in the date picker.
380 | /// - Parameters:
381 | /// - color: The color of the button.
382 | /// - font: The font of the button.
383 | /// - Returns: An updated SSDatePicker instance.
384 | public func buttonStyle(color: Color? = nil, font: Font? = nil) -> SSDatePicker {
385 | var picker = self
386 | color.map { picker.configuration.buttonStyle.color = $0 }
387 | font.map { picker.configuration.buttonStyle.font = $0 }
388 | return picker
389 | }
390 |
391 | /// Sets the style for the current month and year label in the date picker.
392 | /// - Parameters:
393 | /// - color: The color of the current month and year label.
394 | /// - font: The font of the current month and year label.
395 | /// - Returns: An updated SSDatePicker instance.
396 | public func currentMonthYearLabelStyle(color: Color? = nil, font: Font? = nil) -> SSDatePicker {
397 | var picker = self
398 | color.map { picker.configuration.currentMonthYearLabelStyle.color = $0 }
399 | font.map { picker.configuration.currentMonthYearLabelStyle.font = $0 }
400 | return picker
401 | }
402 |
403 | /// Sets the color for the selected date in the date picker.
404 | /// - Parameters:
405 | /// - backgroundColor: The background color of the selected date.
406 | /// - foregroundColor: The foreground color of the selected date.
407 | /// - Returns: An updated SSDatePicker instance.
408 | public func selectedDateColor(backgroundColor: Color? = nil, foregroundColor: Color? = nil) -> SSDatePicker {
409 | var picker = self
410 | backgroundColor.map { picker.configuration.selectedDateColor.backgroundColor = $0 }
411 | foregroundColor.map { picker.configuration.selectedDateColor.foregroundColor = $0 }
412 | return picker
413 | }
414 |
415 | /// Sets the color for today's date in the date picker.
416 | /// - Parameters:
417 | /// - backgroundColor: The background color of today's date.
418 | /// - foregroundColor: The foreground color of today's date.
419 | /// - Returns: An updated SSDatePicker instance.
420 | public func todayColor(backgroundColor: Color? = nil, foregroundColor: Color? = nil) -> SSDatePicker {
421 | var picker = self
422 | picker.configuration.todayDateColor.backgroundColor = backgroundColor
423 | picker.configuration.todayDateColor.foregroundColor = foregroundColor
424 | return picker
425 | }
426 |
427 | /// Sets the color for today's date when it is selected in the date picker.
428 | /// - Parameters:
429 | /// - backgroundColor: The background color of the selected today's date.
430 | /// - foregroundColor: The foreground color of the selected today's date.
431 | /// - Returns: An updated SSDatePicker instance.
432 | public func todayDateSelectionColor(backgroundColor: Color? = nil, foregroundColor: Color? = nil) -> SSDatePicker {
433 | var picker = self
434 | picker.configuration.todayDateSelectionColor.backgroundColor = backgroundColor
435 | picker.configuration.todayDateSelectionColor.foregroundColor = foregroundColor
436 | return picker
437 | }
438 |
439 | /// Sets the background color for the date picker.
440 | /// - Parameter color: The background color of the date picker.
441 | /// - Returns: An updated SSDatePicker instance.
442 | public func pickerBackgroundColor(_ color: Color) -> SSDatePicker {
443 | var picker = self
444 | picker.configuration.pickerBackgroundColor = color
445 | return picker
446 | }
447 |
448 | /// Sets the color for the separator line between date components in the date picker.
449 | /// - Parameter color: The color of the separator line.
450 | /// - Returns: An updated SSDatePicker instance.
451 | public func sepratorLineColor(_ color: Color) -> SSDatePicker {
452 | var picker = self
453 | picker.configuration.sepratorLineColor = color
454 | return picker
455 | }
456 |
457 | /// Sets the overlay color for the background of the entire date picker.
458 | /// - Parameter color: The color of the overlay.
459 | /// - Returns: An updated SSDatePicker instance.
460 | public func popupOverlayColor(_ color: Color) -> SSDatePicker {
461 | var picker = self
462 | picker.configuration.popupOverlayColor = color
463 | return picker
464 | }
465 |
466 | /// Sets the minimum selectable date in the date picker.
467 | /// - Parameter date: The minimum selectable date.
468 | /// - Returns: An updated SSDatePicker instance.
469 | public func minimumDate(_ date: Date) -> SSDatePicker {
470 | var picker = self
471 | picker.configuration.minimumDate = date
472 | return picker
473 | }
474 |
475 | /// Sets the maximum selectable date in the date picker.
476 | /// - Parameter date: The maximum selectable date.
477 | /// - Returns: An updated SSDatePicker instance.
478 | public func maximumDate(_ date: Date) -> SSDatePicker {
479 | var picker = self
480 | picker.configuration.maximumDate = date
481 | return picker
482 | }
483 |
484 | /// Disables selection of past dates in the date picker.
485 | /// - Returns: An updated SSDatePicker instance.
486 | public func disablePastDate() -> SSDatePicker {
487 | var picker = self
488 | picker.configuration.minimumDate = Date()
489 | return picker
490 | }
491 |
492 | /// Disables selection of future dates in the date picker.
493 | /// - Returns: An updated SSDatePicker instance.
494 | public func disableFutureDate() -> SSDatePicker {
495 | var picker = self
496 | picker.configuration.maximumDate = Date()
497 | return picker
498 | }
499 |
500 | /// Sets the calendar used by the date picker.
501 | /// - Parameter calendar: The calendar to be used.
502 | /// - Returns: An updated SSDatePicker instance.
503 | public func calendar(_ calendar: Calendar) -> SSDatePicker {
504 | var picker = self
505 | picker.configuration.calendar = calendar
506 | return picker
507 | }
508 |
509 | /// Sets the callback closure to be executed when a date is selected in the date picker.
510 | /// - Parameter completion: The closure to be executed.
511 | /// - Returns: An updated SSDatePicker instance.
512 | public func onDateSelection(_ completion: @escaping (Date) -> ()) -> SSDatePicker {
513 | let picker = self
514 | picker.datePickerManager.dateSelectionCallback = completion
515 | return picker
516 | }
517 |
518 | /// The current month displayed in the date picker.
519 | /// Setting this property will determine the month whose calendar dates will be displayed when the picker is opened.
520 | public func currentMonth(_ date: Date) -> SSDatePicker {
521 | let picker = self
522 | picker.datePickerManager.currentMonth = date
523 | return picker
524 | }
525 |
526 | /// The selected date in the date picker. Set this property to pre-select a specific date.
527 | public func selectedDate(_ date: Date?) -> SSDatePicker {
528 | let picker = self
529 | picker.datePickerManager.selectedDate = date
530 | return picker
531 | }
532 |
533 | /// Sets the disable dates in the date picker.
534 | /// - Parameter dates: All dates that should be disable.
535 | /// - Returns: An updated SSDatePicker instance.
536 | public func disableDates(_ dates: [Date]) -> SSDatePicker {
537 | let picker = self
538 | picker.datePickerManager.disableDates = dates
539 | return picker
540 | }
541 |
542 | /// Enables date range selection in the date picker.
543 | /// - Returns: An SSDateRangePicker instance for configuring date range selection.
544 | public func enableDateRangeSelection() -> SSDateRangePicker {
545 | var picker = self
546 | picker.configuration.allowRangeSelection = true
547 | return SSDateRangePicker(picker)
548 | }
549 |
550 | /// Enables multiple date selection in the date picker.
551 | /// - Returns: An SSMultiDatePicker instance for configuring multiple date selection.
552 | public func enableMultipleDateSelection() -> SSMultiDatePicker {
553 | var picker = self
554 | picker.configuration.allowMultipleSelection = true
555 | return SSMultiDatePicker(picker)
556 | }
557 |
558 | }
559 |
--------------------------------------------------------------------------------
/Sources/SSDateTimePicker/SSDateTimePicker.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 6507209C2B0F4D4500AC1FB6 /* SSDatePickerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6507209B2B0F4D4500AC1FB6 /* SSDatePickerConfiguration.swift */; };
11 | 6507209E2B0F538400AC1FB6 /* SSPickerConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6507209D2B0F538400AC1FB6 /* SSPickerConstants.swift */; };
12 | 650720A22B0F644000AC1FB6 /* SSWeekDatesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720A12B0F644000AC1FB6 /* SSWeekDatesView.swift */; };
13 | 650720A62B0F6BE600AC1FB6 /* SSDateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720A52B0F6BE600AC1FB6 /* SSDateView.swift */; };
14 | 650720AA2B0F819F00AC1FB6 /* SSImageConstant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720A92B0F819F00AC1FB6 /* SSImageConstant.swift */; };
15 | 650720B12B107F6C00AC1FB6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 650720B02B107F6C00AC1FB6 /* Assets.xcassets */; };
16 | 650720B32B10880900AC1FB6 /* SSMonthSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720B22B10880900AC1FB6 /* SSMonthSelectionView.swift */; };
17 | 650720B52B10881900AC1FB6 /* SSYearSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720B42B10881900AC1FB6 /* SSYearSelectionView.swift */; };
18 | 650720B82B10CB0300AC1FB6 /* SSCalendar+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720B72B10CB0300AC1FB6 /* SSCalendar+Extension.swift */; };
19 | 650720BA2B10CB2800AC1FB6 /* SSDateComponents+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720B92B10CB2800AC1FB6 /* SSDateComponents+Extension.swift */; };
20 | 650720BC2B10CB5000AC1FB6 /* SSDate+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720BB2B10CB5000AC1FB6 /* SSDate+Extension.swift */; };
21 | 650720BE2B10CB7300AC1FB6 /* SSDateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720BD2B10CB7300AC1FB6 /* SSDateFormatter+Extension.swift */; };
22 | 650720C22B10CBC500AC1FB6 /* SSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720C12B10CBC500AC1FB6 /* SSColor+Extension.swift */; };
23 | 650720C62B13BE4D00AC1FB6 /* SSDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720C52B13BE4D00AC1FB6 /* SSDatePicker.swift */; };
24 | 650720C82B146E9500AC1FB6 /* SSDatePickerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720C72B146E9500AC1FB6 /* SSDatePickerManager.swift */; };
25 | 650720CB2B181E5400AC1FB6 /* SSView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720CA2B181E5400AC1FB6 /* SSView+Extension.swift */; };
26 | 650720CD2B18257600AC1FB6 /* SSCornerRadiusStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720CC2B18257600AC1FB6 /* SSCornerRadiusStyle.swift */; };
27 | 650720EF2B19FD7A00AC1FB6 /* SSThemeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720EE2B19FD7A00AC1FB6 /* SSThemeButton.swift */; };
28 | 650720F52B1DAFFB00AC1FB6 /* SSTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720F42B1DAFFB00AC1FB6 /* SSTimePicker.swift */; };
29 | 650720F72B1DDF3600AC1FB6 /* SSTimePickerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720F62B1DDF3600AC1FB6 /* SSTimePickerManager.swift */; };
30 | 650720F92B1DE9FD00AC1FB6 /* SSTimeTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720F82B1DE9FD00AC1FB6 /* SSTimeTextField.swift */; };
31 | 650720FB2B1DEF8300AC1FB6 /* SSTimePickerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650720FA2B1DEF8300AC1FB6 /* SSTimePickerConfiguration.swift */; };
32 | 651668582B20842000AD02A1 /* SSInt+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 651668572B20842000AD02A1 /* SSInt+Extension.swift */; };
33 | 654A7F952B1F04DF00EB9B33 /* SSClockPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 654A7F942B1F04DF00EB9B33 /* SSClockPicker.swift */; };
34 | 6579912D2B3016D100BE8B25 /* SSDateRangePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6579912C2B3016D100BE8B25 /* SSDateRangePicker.swift */; };
35 | 6579912F2B3016F900BE8B25 /* SSMultiDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6579912E2B3016F900BE8B25 /* SSMultiDatePicker.swift */; };
36 | 657991332B30834400BE8B25 /* SSUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657991322B30834400BE8B25 /* SSUtils.swift */; };
37 | 65AB6AF22B28205E009EA7EC /* SSLocalizedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AB6AF12B28205E009EA7EC /* SSLocalizedString.swift */; };
38 | 65AB6AF42B282280009EA7EC /* SSString+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AB6AF32B282280009EA7EC /* SSString+Extension.swift */; };
39 | 65AB6B032B282A18009EA7EC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 65AB6B052B282A18009EA7EC /* Localizable.strings */; };
40 | 65E058102B0E2B260049A7BA /* DateTimePicker.docc in Sources */ = {isa = PBXBuildFile; fileRef = 65E0580F2B0E2B260049A7BA /* DateTimePicker.docc */; };
41 | 65E058162B0E2B260049A7BA /* SSDateTimePicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65E0580B2B0E2B260049A7BA /* SSDateTimePicker.framework */; };
42 | 65E0581C2B0E2B260049A7BA /* DateTimePicker.h in Headers */ = {isa = PBXBuildFile; fileRef = 65E0580E2B0E2B260049A7BA /* DateTimePicker.h */; settings = {ATTRIBUTES = (Public, ); }; };
43 | /* End PBXBuildFile section */
44 |
45 | /* Begin PBXContainerItemProxy section */
46 | 65E058172B0E2B260049A7BA /* PBXContainerItemProxy */ = {
47 | isa = PBXContainerItemProxy;
48 | containerPortal = 65E058022B0E2B260049A7BA /* Project object */;
49 | proxyType = 1;
50 | remoteGlobalIDString = 65E0580A2B0E2B260049A7BA;
51 | remoteInfo = DateTimePicker;
52 | };
53 | /* End PBXContainerItemProxy section */
54 |
55 | /* Begin PBXFileReference section */
56 | 6507209B2B0F4D4500AC1FB6 /* SSDatePickerConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSDatePickerConfiguration.swift; sourceTree = ""; };
57 | 6507209D2B0F538400AC1FB6 /* SSPickerConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSPickerConstants.swift; sourceTree = ""; };
58 | 650720A12B0F644000AC1FB6 /* SSWeekDatesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSWeekDatesView.swift; sourceTree = ""; };
59 | 650720A52B0F6BE600AC1FB6 /* SSDateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSDateView.swift; sourceTree = ""; };
60 | 650720A92B0F819F00AC1FB6 /* SSImageConstant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSImageConstant.swift; sourceTree = ""; };
61 | 650720B02B107F6C00AC1FB6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
62 | 650720B22B10880900AC1FB6 /* SSMonthSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSMonthSelectionView.swift; sourceTree = ""; };
63 | 650720B42B10881900AC1FB6 /* SSYearSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSYearSelectionView.swift; sourceTree = ""; };
64 | 650720B72B10CB0300AC1FB6 /* SSCalendar+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SSCalendar+Extension.swift"; sourceTree = ""; };
65 | 650720B92B10CB2800AC1FB6 /* SSDateComponents+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SSDateComponents+Extension.swift"; sourceTree = ""; };
66 | 650720BB2B10CB5000AC1FB6 /* SSDate+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SSDate+Extension.swift"; sourceTree = ""; };
67 | 650720BD2B10CB7300AC1FB6 /* SSDateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SSDateFormatter+Extension.swift"; sourceTree = ""; };
68 | 650720C12B10CBC500AC1FB6 /* SSColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SSColor+Extension.swift"; sourceTree = ""; };
69 | 650720C52B13BE4D00AC1FB6 /* SSDatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSDatePicker.swift; sourceTree = ""; };
70 | 650720C72B146E9500AC1FB6 /* SSDatePickerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSDatePickerManager.swift; sourceTree = ""; };
71 | 650720CA2B181E5400AC1FB6 /* SSView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SSView+Extension.swift"; sourceTree = ""; };
72 | 650720CC2B18257600AC1FB6 /* SSCornerRadiusStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSCornerRadiusStyle.swift; sourceTree = ""; };
73 | 650720EE2B19FD7A00AC1FB6 /* SSThemeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSThemeButton.swift; sourceTree = ""; };
74 | 650720F42B1DAFFB00AC1FB6 /* SSTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSTimePicker.swift; sourceTree = ""; };
75 | 650720F62B1DDF3600AC1FB6 /* SSTimePickerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSTimePickerManager.swift; sourceTree = ""; };
76 | 650720F82B1DE9FD00AC1FB6 /* SSTimeTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSTimeTextField.swift; sourceTree = ""; };
77 | 650720FA2B1DEF8300AC1FB6 /* SSTimePickerConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSTimePickerConfiguration.swift; sourceTree = ""; };
78 | 651668572B20842000AD02A1 /* SSInt+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SSInt+Extension.swift"; sourceTree = ""; };
79 | 654A7F942B1F04DF00EB9B33 /* SSClockPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSClockPicker.swift; sourceTree = ""; };
80 | 6579912C2B3016D100BE8B25 /* SSDateRangePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSDateRangePicker.swift; sourceTree = ""; };
81 | 6579912E2B3016F900BE8B25 /* SSMultiDatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSMultiDatePicker.swift; sourceTree = ""; };
82 | 657991322B30834400BE8B25 /* SSUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSUtils.swift; sourceTree = ""; };
83 | 65AB6AF12B28205E009EA7EC /* SSLocalizedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSLocalizedString.swift; sourceTree = ""; };
84 | 65AB6AF32B282280009EA7EC /* SSString+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SSString+Extension.swift"; sourceTree = ""; };
85 | 65AB6B042B282A18009EA7EC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; };
86 | 65E0580B2B0E2B260049A7BA /* SSDateTimePicker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SSDateTimePicker.framework; sourceTree = BUILT_PRODUCTS_DIR; };
87 | 65E0580E2B0E2B260049A7BA /* DateTimePicker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DateTimePicker.h; sourceTree = ""; };
88 | 65E0580F2B0E2B260049A7BA /* DateTimePicker.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = DateTimePicker.docc; sourceTree = ""; };
89 | 65E058152B0E2B260049A7BA /* SSDateTimePickerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SSDateTimePickerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
90 | /* End PBXFileReference section */
91 |
92 | /* Begin PBXFrameworksBuildPhase section */
93 | 65E058082B0E2B260049A7BA /* Frameworks */ = {
94 | isa = PBXFrameworksBuildPhase;
95 | buildActionMask = 2147483647;
96 | files = (
97 | );
98 | runOnlyForDeploymentPostprocessing = 0;
99 | };
100 | 65E058122B0E2B260049A7BA /* Frameworks */ = {
101 | isa = PBXFrameworksBuildPhase;
102 | buildActionMask = 2147483647;
103 | files = (
104 | 65E058162B0E2B260049A7BA /* SSDateTimePicker.framework in Frameworks */,
105 | );
106 | runOnlyForDeploymentPostprocessing = 0;
107 | };
108 | /* End PBXFrameworksBuildPhase section */
109 |
110 | /* Begin PBXGroup section */
111 | 650720A32B0F645000AC1FB6 /* Views */ = {
112 | isa = PBXGroup;
113 | children = (
114 | 650720EE2B19FD7A00AC1FB6 /* SSThemeButton.swift */,
115 | 6516685A2B21F5F500AD02A1 /* Time Picker */,
116 | 651668592B21F5ED00AD02A1 /* Date picker */,
117 | );
118 | path = Views;
119 | sourceTree = "";
120 | };
121 | 650720A42B0F645600AC1FB6 /* Common */ = {
122 | isa = PBXGroup;
123 | children = (
124 | 654A7F972B1F2CF400EB9B33 /* Time picker */,
125 | 654A7F962B1F2CEC00EB9B33 /* Date picker */,
126 | 6507209D2B0F538400AC1FB6 /* SSPickerConstants.swift */,
127 | 650720A92B0F819F00AC1FB6 /* SSImageConstant.swift */,
128 | 650720CC2B18257600AC1FB6 /* SSCornerRadiusStyle.swift */,
129 | 65AB6AF12B28205E009EA7EC /* SSLocalizedString.swift */,
130 | 657991322B30834400BE8B25 /* SSUtils.swift */,
131 | );
132 | path = Common;
133 | sourceTree = "";
134 | };
135 | 650720B62B10CABF00AC1FB6 /* Extensions */ = {
136 | isa = PBXGroup;
137 | children = (
138 | 650720B72B10CB0300AC1FB6 /* SSCalendar+Extension.swift */,
139 | 650720B92B10CB2800AC1FB6 /* SSDateComponents+Extension.swift */,
140 | 650720BB2B10CB5000AC1FB6 /* SSDate+Extension.swift */,
141 | 650720BD2B10CB7300AC1FB6 /* SSDateFormatter+Extension.swift */,
142 | 650720C12B10CBC500AC1FB6 /* SSColor+Extension.swift */,
143 | 650720CA2B181E5400AC1FB6 /* SSView+Extension.swift */,
144 | 651668572B20842000AD02A1 /* SSInt+Extension.swift */,
145 | 65AB6AF32B282280009EA7EC /* SSString+Extension.swift */,
146 | );
147 | path = Extensions;
148 | sourceTree = "";
149 | };
150 | 651668592B21F5ED00AD02A1 /* Date picker */ = {
151 | isa = PBXGroup;
152 | children = (
153 | 650720C52B13BE4D00AC1FB6 /* SSDatePicker.swift */,
154 | 650720B42B10881900AC1FB6 /* SSYearSelectionView.swift */,
155 | 650720B22B10880900AC1FB6 /* SSMonthSelectionView.swift */,
156 | 650720A52B0F6BE600AC1FB6 /* SSDateView.swift */,
157 | 650720A12B0F644000AC1FB6 /* SSWeekDatesView.swift */,
158 | 6579912C2B3016D100BE8B25 /* SSDateRangePicker.swift */,
159 | 6579912E2B3016F900BE8B25 /* SSMultiDatePicker.swift */,
160 | );
161 | path = "Date picker";
162 | sourceTree = "";
163 | };
164 | 6516685A2B21F5F500AD02A1 /* Time Picker */ = {
165 | isa = PBXGroup;
166 | children = (
167 | 654A7F942B1F04DF00EB9B33 /* SSClockPicker.swift */,
168 | 650720F82B1DE9FD00AC1FB6 /* SSTimeTextField.swift */,
169 | 650720F42B1DAFFB00AC1FB6 /* SSTimePicker.swift */,
170 | );
171 | path = "Time Picker";
172 | sourceTree = "";
173 | };
174 | 654A7F962B1F2CEC00EB9B33 /* Date picker */ = {
175 | isa = PBXGroup;
176 | children = (
177 | 650720C72B146E9500AC1FB6 /* SSDatePickerManager.swift */,
178 | 6507209B2B0F4D4500AC1FB6 /* SSDatePickerConfiguration.swift */,
179 | );
180 | path = "Date picker";
181 | sourceTree = "";
182 | };
183 | 654A7F972B1F2CF400EB9B33 /* Time picker */ = {
184 | isa = PBXGroup;
185 | children = (
186 | 650720FA2B1DEF8300AC1FB6 /* SSTimePickerConfiguration.swift */,
187 | 650720F62B1DDF3600AC1FB6 /* SSTimePickerManager.swift */,
188 | );
189 | path = "Time picker";
190 | sourceTree = "";
191 | };
192 | 65E058012B0E2B260049A7BA = {
193 | isa = PBXGroup;
194 | children = (
195 | 65E0580D2B0E2B260049A7BA /* SSDateTimePicker */,
196 | 65E0580C2B0E2B260049A7BA /* Products */,
197 | );
198 | sourceTree = "";
199 | };
200 | 65E0580C2B0E2B260049A7BA /* Products */ = {
201 | isa = PBXGroup;
202 | children = (
203 | 65E0580B2B0E2B260049A7BA /* SSDateTimePicker.framework */,
204 | 65E058152B0E2B260049A7BA /* SSDateTimePickerTests.xctest */,
205 | );
206 | name = Products;
207 | sourceTree = "";
208 | };
209 | 65E0580D2B0E2B260049A7BA /* SSDateTimePicker */ = {
210 | isa = PBXGroup;
211 | children = (
212 | 65AB6B052B282A18009EA7EC /* Localizable.strings */,
213 | 650720B02B107F6C00AC1FB6 /* Assets.xcassets */,
214 | 650720B62B10CABF00AC1FB6 /* Extensions */,
215 | 650720A42B0F645600AC1FB6 /* Common */,
216 | 650720A32B0F645000AC1FB6 /* Views */,
217 | 65E0580E2B0E2B260049A7BA /* DateTimePicker.h */,
218 | 65E0580F2B0E2B260049A7BA /* DateTimePicker.docc */,
219 | );
220 | path = SSDateTimePicker;
221 | sourceTree = "";
222 | };
223 | /* End PBXGroup section */
224 |
225 | /* Begin PBXHeadersBuildPhase section */
226 | 65E058062B0E2B260049A7BA /* Headers */ = {
227 | isa = PBXHeadersBuildPhase;
228 | buildActionMask = 2147483647;
229 | files = (
230 | 65E0581C2B0E2B260049A7BA /* DateTimePicker.h in Headers */,
231 | );
232 | runOnlyForDeploymentPostprocessing = 0;
233 | };
234 | /* End PBXHeadersBuildPhase section */
235 |
236 | /* Begin PBXNativeTarget section */
237 | 65E0580A2B0E2B260049A7BA /* SSDateTimePicker */ = {
238 | isa = PBXNativeTarget;
239 | buildConfigurationList = 65E0581F2B0E2B260049A7BA /* Build configuration list for PBXNativeTarget "SSDateTimePicker" */;
240 | buildPhases = (
241 | 65E058062B0E2B260049A7BA /* Headers */,
242 | 65E058072B0E2B260049A7BA /* Sources */,
243 | 65E058082B0E2B260049A7BA /* Frameworks */,
244 | 65E058092B0E2B260049A7BA /* Resources */,
245 | );
246 | buildRules = (
247 | );
248 | dependencies = (
249 | );
250 | name = SSDateTimePicker;
251 | productName = DateTimePicker;
252 | productReference = 65E0580B2B0E2B260049A7BA /* SSDateTimePicker.framework */;
253 | productType = "com.apple.product-type.framework";
254 | };
255 | 65E058142B0E2B260049A7BA /* SSDateTimePickerTests */ = {
256 | isa = PBXNativeTarget;
257 | buildConfigurationList = 65E058222B0E2B260049A7BA /* Build configuration list for PBXNativeTarget "SSDateTimePickerTests" */;
258 | buildPhases = (
259 | 65E058112B0E2B260049A7BA /* Sources */,
260 | 65E058122B0E2B260049A7BA /* Frameworks */,
261 | 65E058132B0E2B260049A7BA /* Resources */,
262 | );
263 | buildRules = (
264 | );
265 | dependencies = (
266 | 65E058182B0E2B260049A7BA /* PBXTargetDependency */,
267 | );
268 | name = SSDateTimePickerTests;
269 | productName = DateTimePickerTests;
270 | productReference = 65E058152B0E2B260049A7BA /* SSDateTimePickerTests.xctest */;
271 | productType = "com.apple.product-type.bundle.unit-test";
272 | };
273 | /* End PBXNativeTarget section */
274 |
275 | /* Begin PBXProject section */
276 | 65E058022B0E2B260049A7BA /* Project object */ = {
277 | isa = PBXProject;
278 | attributes = {
279 | BuildIndependentTargetsInParallel = 1;
280 | LastSwiftUpdateCheck = 1430;
281 | LastUpgradeCheck = 1430;
282 | TargetAttributes = {
283 | 65E0580A2B0E2B260049A7BA = {
284 | CreatedOnToolsVersion = 14.3.1;
285 | };
286 | 65E058142B0E2B260049A7BA = {
287 | CreatedOnToolsVersion = 14.3.1;
288 | };
289 | };
290 | };
291 | buildConfigurationList = 65E058052B0E2B260049A7BA /* Build configuration list for PBXProject "SSDateTimePicker" */;
292 | compatibilityVersion = "Xcode 14.0";
293 | developmentRegion = en;
294 | hasScannedForEncodings = 0;
295 | knownRegions = (
296 | en,
297 | Base,
298 | );
299 | mainGroup = 65E058012B0E2B260049A7BA;
300 | productRefGroup = 65E0580C2B0E2B260049A7BA /* Products */;
301 | projectDirPath = "";
302 | projectRoot = "";
303 | targets = (
304 | 65E0580A2B0E2B260049A7BA /* SSDateTimePicker */,
305 | 65E058142B0E2B260049A7BA /* SSDateTimePickerTests */,
306 | );
307 | };
308 | /* End PBXProject section */
309 |
310 | /* Begin PBXResourcesBuildPhase section */
311 | 65E058092B0E2B260049A7BA /* Resources */ = {
312 | isa = PBXResourcesBuildPhase;
313 | buildActionMask = 2147483647;
314 | files = (
315 | 650720B12B107F6C00AC1FB6 /* Assets.xcassets in Resources */,
316 | 65AB6B032B282A18009EA7EC /* Localizable.strings in Resources */,
317 | );
318 | runOnlyForDeploymentPostprocessing = 0;
319 | };
320 | 65E058132B0E2B260049A7BA /* Resources */ = {
321 | isa = PBXResourcesBuildPhase;
322 | buildActionMask = 2147483647;
323 | files = (
324 | );
325 | runOnlyForDeploymentPostprocessing = 0;
326 | };
327 | /* End PBXResourcesBuildPhase section */
328 |
329 | /* Begin PBXSourcesBuildPhase section */
330 | 65E058072B0E2B260049A7BA /* Sources */ = {
331 | isa = PBXSourcesBuildPhase;
332 | buildActionMask = 2147483647;
333 | files = (
334 | 650720BA2B10CB2800AC1FB6 /* SSDateComponents+Extension.swift in Sources */,
335 | 650720A22B0F644000AC1FB6 /* SSWeekDatesView.swift in Sources */,
336 | 650720B82B10CB0300AC1FB6 /* SSCalendar+Extension.swift in Sources */,
337 | 650720CB2B181E5400AC1FB6 /* SSView+Extension.swift in Sources */,
338 | 650720A62B0F6BE600AC1FB6 /* SSDateView.swift in Sources */,
339 | 650720C62B13BE4D00AC1FB6 /* SSDatePicker.swift in Sources */,
340 | 650720B32B10880900AC1FB6 /* SSMonthSelectionView.swift in Sources */,
341 | 650720CD2B18257600AC1FB6 /* SSCornerRadiusStyle.swift in Sources */,
342 | 650720AA2B0F819F00AC1FB6 /* SSImageConstant.swift in Sources */,
343 | 651668582B20842000AD02A1 /* SSInt+Extension.swift in Sources */,
344 | 650720BE2B10CB7300AC1FB6 /* SSDateFormatter+Extension.swift in Sources */,
345 | 650720B52B10881900AC1FB6 /* SSYearSelectionView.swift in Sources */,
346 | 6579912D2B3016D100BE8B25 /* SSDateRangePicker.swift in Sources */,
347 | 654A7F952B1F04DF00EB9B33 /* SSClockPicker.swift in Sources */,
348 | 650720F72B1DDF3600AC1FB6 /* SSTimePickerManager.swift in Sources */,
349 | 650720C82B146E9500AC1FB6 /* SSDatePickerManager.swift in Sources */,
350 | 650720F92B1DE9FD00AC1FB6 /* SSTimeTextField.swift in Sources */,
351 | 6507209E2B0F538400AC1FB6 /* SSPickerConstants.swift in Sources */,
352 | 65AB6AF22B28205E009EA7EC /* SSLocalizedString.swift in Sources */,
353 | 650720FB2B1DEF8300AC1FB6 /* SSTimePickerConfiguration.swift in Sources */,
354 | 65E058102B0E2B260049A7BA /* DateTimePicker.docc in Sources */,
355 | 650720BC2B10CB5000AC1FB6 /* SSDate+Extension.swift in Sources */,
356 | 650720F52B1DAFFB00AC1FB6 /* SSTimePicker.swift in Sources */,
357 | 650720EF2B19FD7A00AC1FB6 /* SSThemeButton.swift in Sources */,
358 | 650720C22B10CBC500AC1FB6 /* SSColor+Extension.swift in Sources */,
359 | 657991332B30834400BE8B25 /* SSUtils.swift in Sources */,
360 | 65AB6AF42B282280009EA7EC /* SSString+Extension.swift in Sources */,
361 | 6507209C2B0F4D4500AC1FB6 /* SSDatePickerConfiguration.swift in Sources */,
362 | 6579912F2B3016F900BE8B25 /* SSMultiDatePicker.swift in Sources */,
363 | );
364 | runOnlyForDeploymentPostprocessing = 0;
365 | };
366 | 65E058112B0E2B260049A7BA /* Sources */ = {
367 | isa = PBXSourcesBuildPhase;
368 | buildActionMask = 2147483647;
369 | files = (
370 | );
371 | runOnlyForDeploymentPostprocessing = 0;
372 | };
373 | /* End PBXSourcesBuildPhase section */
374 |
375 | /* Begin PBXTargetDependency section */
376 | 65E058182B0E2B260049A7BA /* PBXTargetDependency */ = {
377 | isa = PBXTargetDependency;
378 | target = 65E0580A2B0E2B260049A7BA /* SSDateTimePicker */;
379 | targetProxy = 65E058172B0E2B260049A7BA /* PBXContainerItemProxy */;
380 | };
381 | /* End PBXTargetDependency section */
382 |
383 | /* Begin PBXVariantGroup section */
384 | 65AB6B052B282A18009EA7EC /* Localizable.strings */ = {
385 | isa = PBXVariantGroup;
386 | children = (
387 | 65AB6B042B282A18009EA7EC /* en */,
388 | );
389 | name = Localizable.strings;
390 | sourceTree = "";
391 | };
392 | /* End PBXVariantGroup section */
393 |
394 | /* Begin XCBuildConfiguration section */
395 | 65E0581D2B0E2B260049A7BA /* Debug */ = {
396 | isa = XCBuildConfiguration;
397 | buildSettings = {
398 | ALWAYS_SEARCH_USER_PATHS = NO;
399 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
400 | CLANG_ANALYZER_NONNULL = YES;
401 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
402 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
403 | CLANG_ENABLE_MODULES = YES;
404 | CLANG_ENABLE_OBJC_ARC = YES;
405 | CLANG_ENABLE_OBJC_WEAK = YES;
406 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
407 | CLANG_WARN_BOOL_CONVERSION = YES;
408 | CLANG_WARN_COMMA = YES;
409 | CLANG_WARN_CONSTANT_CONVERSION = YES;
410 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
411 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
412 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
413 | CLANG_WARN_EMPTY_BODY = YES;
414 | CLANG_WARN_ENUM_CONVERSION = YES;
415 | CLANG_WARN_INFINITE_RECURSION = YES;
416 | CLANG_WARN_INT_CONVERSION = YES;
417 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
418 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
419 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
420 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
421 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
422 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
423 | CLANG_WARN_STRICT_PROTOTYPES = YES;
424 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
425 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
426 | CLANG_WARN_UNREACHABLE_CODE = YES;
427 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
428 | COPY_PHASE_STRIP = NO;
429 | CURRENT_PROJECT_VERSION = 1;
430 | DEBUG_INFORMATION_FORMAT = dwarf;
431 | ENABLE_STRICT_OBJC_MSGSEND = YES;
432 | ENABLE_TESTABILITY = YES;
433 | GCC_C_LANGUAGE_STANDARD = gnu11;
434 | GCC_DYNAMIC_NO_PIC = NO;
435 | GCC_NO_COMMON_BLOCKS = YES;
436 | GCC_OPTIMIZATION_LEVEL = 0;
437 | GCC_PREPROCESSOR_DEFINITIONS = (
438 | "DEBUG=1",
439 | "$(inherited)",
440 | );
441 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
442 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
443 | GCC_WARN_UNDECLARED_SELECTOR = YES;
444 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
445 | GCC_WARN_UNUSED_FUNCTION = YES;
446 | GCC_WARN_UNUSED_VARIABLE = YES;
447 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
448 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
449 | MTL_FAST_MATH = YES;
450 | ONLY_ACTIVE_ARCH = YES;
451 | SDKROOT = iphoneos;
452 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
453 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
454 | VERSIONING_SYSTEM = "apple-generic";
455 | VERSION_INFO_PREFIX = "";
456 | };
457 | name = Debug;
458 | };
459 | 65E0581E2B0E2B260049A7BA /* Release */ = {
460 | isa = XCBuildConfiguration;
461 | buildSettings = {
462 | ALWAYS_SEARCH_USER_PATHS = NO;
463 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
464 | CLANG_ANALYZER_NONNULL = YES;
465 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
466 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
467 | CLANG_ENABLE_MODULES = YES;
468 | CLANG_ENABLE_OBJC_ARC = YES;
469 | CLANG_ENABLE_OBJC_WEAK = YES;
470 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
471 | CLANG_WARN_BOOL_CONVERSION = YES;
472 | CLANG_WARN_COMMA = YES;
473 | CLANG_WARN_CONSTANT_CONVERSION = YES;
474 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
475 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
476 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
477 | CLANG_WARN_EMPTY_BODY = YES;
478 | CLANG_WARN_ENUM_CONVERSION = YES;
479 | CLANG_WARN_INFINITE_RECURSION = YES;
480 | CLANG_WARN_INT_CONVERSION = YES;
481 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
482 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
483 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
484 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
485 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
486 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
487 | CLANG_WARN_STRICT_PROTOTYPES = YES;
488 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
489 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
490 | CLANG_WARN_UNREACHABLE_CODE = YES;
491 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
492 | COPY_PHASE_STRIP = NO;
493 | CURRENT_PROJECT_VERSION = 1;
494 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
495 | ENABLE_NS_ASSERTIONS = NO;
496 | ENABLE_STRICT_OBJC_MSGSEND = YES;
497 | GCC_C_LANGUAGE_STANDARD = gnu11;
498 | GCC_NO_COMMON_BLOCKS = YES;
499 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
500 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
501 | GCC_WARN_UNDECLARED_SELECTOR = YES;
502 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
503 | GCC_WARN_UNUSED_FUNCTION = YES;
504 | GCC_WARN_UNUSED_VARIABLE = YES;
505 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
506 | MTL_ENABLE_DEBUG_INFO = NO;
507 | MTL_FAST_MATH = YES;
508 | SDKROOT = iphoneos;
509 | SWIFT_COMPILATION_MODE = wholemodule;
510 | SWIFT_OPTIMIZATION_LEVEL = "-O";
511 | VALIDATE_PRODUCT = YES;
512 | VERSIONING_SYSTEM = "apple-generic";
513 | VERSION_INFO_PREFIX = "";
514 | };
515 | name = Release;
516 | };
517 | 65E058202B0E2B260049A7BA /* Debug */ = {
518 | isa = XCBuildConfiguration;
519 | buildSettings = {
520 | CODE_SIGN_STYLE = Automatic;
521 | CURRENT_PROJECT_VERSION = 1;
522 | DEFINES_MODULE = YES;
523 | DYLIB_COMPATIBILITY_VERSION = 1;
524 | DYLIB_CURRENT_VERSION = 1;
525 | DYLIB_INSTALL_NAME_BASE = "@rpath";
526 | ENABLE_MODULE_VERIFIER = YES;
527 | GENERATE_INFOPLIST_FILE = YES;
528 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
529 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
530 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
531 | LD_RUNPATH_SEARCH_PATHS = (
532 | "$(inherited)",
533 | "@executable_path/Frameworks",
534 | "@loader_path/Frameworks",
535 | );
536 | MARKETING_VERSION = 1.0;
537 | MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
538 | MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20";
539 | PRODUCT_BUNDLE_IDENTIFIER = simform.DateTimePicker;
540 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
541 | SKIP_INSTALL = YES;
542 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
543 | SUPPORTS_MACCATALYST = NO;
544 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
545 | SWIFT_EMIT_LOC_STRINGS = YES;
546 | SWIFT_VERSION = 5.0;
547 | TARGETED_DEVICE_FAMILY = "1,2";
548 | };
549 | name = Debug;
550 | };
551 | 65E058212B0E2B260049A7BA /* Release */ = {
552 | isa = XCBuildConfiguration;
553 | buildSettings = {
554 | CODE_SIGN_STYLE = Automatic;
555 | CURRENT_PROJECT_VERSION = 1;
556 | DEFINES_MODULE = YES;
557 | DYLIB_COMPATIBILITY_VERSION = 1;
558 | DYLIB_CURRENT_VERSION = 1;
559 | DYLIB_INSTALL_NAME_BASE = "@rpath";
560 | ENABLE_MODULE_VERIFIER = YES;
561 | GENERATE_INFOPLIST_FILE = YES;
562 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
563 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
564 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
565 | LD_RUNPATH_SEARCH_PATHS = (
566 | "$(inherited)",
567 | "@executable_path/Frameworks",
568 | "@loader_path/Frameworks",
569 | );
570 | MARKETING_VERSION = 1.0;
571 | MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
572 | MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20";
573 | PRODUCT_BUNDLE_IDENTIFIER = simform.DateTimePicker;
574 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
575 | SKIP_INSTALL = YES;
576 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
577 | SUPPORTS_MACCATALYST = NO;
578 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
579 | SWIFT_EMIT_LOC_STRINGS = YES;
580 | SWIFT_VERSION = 5.0;
581 | TARGETED_DEVICE_FAMILY = "1,2";
582 | };
583 | name = Release;
584 | };
585 | 65E058232B0E2B260049A7BA /* Debug */ = {
586 | isa = XCBuildConfiguration;
587 | buildSettings = {
588 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
589 | CODE_SIGN_STYLE = Automatic;
590 | CURRENT_PROJECT_VERSION = 1;
591 | GENERATE_INFOPLIST_FILE = YES;
592 | MARKETING_VERSION = 1.0;
593 | PRODUCT_BUNDLE_IDENTIFIER = simform.DateTimePickerTests;
594 | PRODUCT_NAME = "$(TARGET_NAME)";
595 | SWIFT_EMIT_LOC_STRINGS = NO;
596 | SWIFT_VERSION = 5.0;
597 | TARGETED_DEVICE_FAMILY = "1,2";
598 | };
599 | name = Debug;
600 | };
601 | 65E058242B0E2B260049A7BA /* Release */ = {
602 | isa = XCBuildConfiguration;
603 | buildSettings = {
604 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
605 | CODE_SIGN_STYLE = Automatic;
606 | CURRENT_PROJECT_VERSION = 1;
607 | GENERATE_INFOPLIST_FILE = YES;
608 | MARKETING_VERSION = 1.0;
609 | PRODUCT_BUNDLE_IDENTIFIER = simform.DateTimePickerTests;
610 | PRODUCT_NAME = "$(TARGET_NAME)";
611 | SWIFT_EMIT_LOC_STRINGS = NO;
612 | SWIFT_VERSION = 5.0;
613 | TARGETED_DEVICE_FAMILY = "1,2";
614 | };
615 | name = Release;
616 | };
617 | /* End XCBuildConfiguration section */
618 |
619 | /* Begin XCConfigurationList section */
620 | 65E058052B0E2B260049A7BA /* Build configuration list for PBXProject "SSDateTimePicker" */ = {
621 | isa = XCConfigurationList;
622 | buildConfigurations = (
623 | 65E0581D2B0E2B260049A7BA /* Debug */,
624 | 65E0581E2B0E2B260049A7BA /* Release */,
625 | );
626 | defaultConfigurationIsVisible = 0;
627 | defaultConfigurationName = Release;
628 | };
629 | 65E0581F2B0E2B260049A7BA /* Build configuration list for PBXNativeTarget "SSDateTimePicker" */ = {
630 | isa = XCConfigurationList;
631 | buildConfigurations = (
632 | 65E058202B0E2B260049A7BA /* Debug */,
633 | 65E058212B0E2B260049A7BA /* Release */,
634 | );
635 | defaultConfigurationIsVisible = 0;
636 | defaultConfigurationName = Release;
637 | };
638 | 65E058222B0E2B260049A7BA /* Build configuration list for PBXNativeTarget "SSDateTimePickerTests" */ = {
639 | isa = XCConfigurationList;
640 | buildConfigurations = (
641 | 65E058232B0E2B260049A7BA /* Debug */,
642 | 65E058242B0E2B260049A7BA /* Release */,
643 | );
644 | defaultConfigurationIsVisible = 0;
645 | defaultConfigurationName = Release;
646 | };
647 | /* End XCConfigurationList section */
648 | };
649 | rootObject = 65E058022B0E2B260049A7BA /* Project object */;
650 | }
651 |
--------------------------------------------------------------------------------