diff --git a/Example/Example For SwiftUI/CalendarView.swift b/Example/Example For SwiftUI/CalendarView.swift new file mode 100644 index 00000000..8698c3bf --- /dev/null +++ b/Example/Example For SwiftUI/CalendarView.swift @@ -0,0 +1,78 @@ +// +// ContentView.swift +// KVKCalendar_Example +// +// Created by Sergei Kviatkovskii on 4/16/22. +// Copyright © 2022 CocoaPods. All rights reserved. +// + +import SwiftUI +import KVKCalendar + +@available(iOS 14.0, *) +struct CalendarView: View { + + @State private var typeCalendar = CalendarType.day + @State private var events: [Event] = [] + @State private var updatedDate: Date? + @State private var orientation: UIInterfaceOrientation = .unknown + @ObservedObject private var viewModel = CalendarViewModel() + + var body: some View { + kvkHandleNavigationView(calendarView) + } + + private var calendarView: some View { + CalendarViewDisplayable(events: $events, + type: $typeCalendar, + updatedDate: $updatedDate, + orientation: $orientation) + .kvkOnRotate(action: { (newOrientation) in + orientation = newOrientation + }) + .onAppear { + viewModel.loadEvents { (items) in + events = items + } + } + .navigationBarTitle("KVKCalendar", displayMode: .inline) + .edgesIgnoringSafeArea(.bottom) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + HStack { + ItemsMenu(type: $typeCalendar, + items: CalendarType.allCases, + showCheckmark: true, + showDropDownIcon: true) + + Button { + updatedDate = Date() + } label: { + Text("Today") + .font(.headline) + .foregroundColor(.red) + } + } + } + ToolbarItem(placement: .navigationBarTrailing) { + Button { + if let event = viewModel.addNewEvent() { + events.append(event) + } + } label: { + Image(systemName: "plus") + .foregroundColor(.red) + } + + } + } + } + +} + +@available(iOS 14.0, *) +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + CalendarView() + } +} diff --git a/Example/Example For SwiftUI/CalendarViewDisplayable.swift b/Example/Example For SwiftUI/CalendarViewDisplayable.swift index 58eb38ff..c26e9945 100644 --- a/Example/Example For SwiftUI/CalendarViewDisplayable.swift +++ b/Example/Example For SwiftUI/CalendarViewDisplayable.swift @@ -10,61 +10,75 @@ import SwiftUI import KVKCalendar import EventKit -@available(iOS 13.0, *) -struct CalendarDisplayView: UIViewRepresentable, KVKCalendarSettings { - +struct CalendarViewDisplayable: UIViewRepresentable, KVKCalendarSettings, KVKCalendarDataModel { + @Binding var events: [Event] @Binding var type: CalendarType @Binding var updatedDate: Date? + @Binding var orientation: UIInterfaceOrientation var style: Style { createCalendarStyle() } var selectDate = Date() - var eventViewer = EventViewer() - private var calendar = CalendarView(frame: .zero) + private var calendar = KVKCalendarView(frame: .zero) - func makeUIView(context: UIViewRepresentableContext) -> CalendarView { + func makeUIView(context: UIViewRepresentableContext) -> KVKCalendarView { calendar.dataSource = context.coordinator calendar.delegate = context.coordinator return calendar } - func updateUIView(_ uiView: CalendarView, context: UIViewRepresentableContext) { + func updateUIView(_ uiView: KVKCalendarView, context: UIViewRepresentableContext) { context.coordinator.events = events context.coordinator.type = type context.coordinator.updatedDate = updatedDate + context.coordinator.orientation = orientation } - func makeCoordinator() -> CalendarDisplayView.Coordinator { + func makeCoordinator() -> CalendarViewDisplayable.Coordinator { Coordinator(self) } - public init(events: Binding<[Event]>, type: Binding, updatedDate: Binding) { - self._events = events - self._type = type - self._updatedDate = updatedDate + public init(events: Binding<[Event]>, + type: Binding, + updatedDate: Binding, + orientation: Binding) { + _events = events + _type = type + _updatedDate = updatedDate + _orientation = orientation selectDate = defaultDate - var frame = UIScreen.main.bounds - frame.origin.y = 0 - frame.size.height -= topOffset - calendar = CalendarView(frame: frame, date: selectDate, style: style) + var frame: CGRect +#if targetEnvironment(macCatalyst) + frame = CGRect(origin: .zero, size: UIApplication.shared.windowSize) +#else + let offset = UIApplication.shared.screenOffset + frame = UIScreen.main.bounds + frame.size.height -= (offset.top + offset.bottom) + frame.size.width -= (offset.right + offset.left) +#endif + calendar = KVKCalendarView(frame: frame, date: selectDate, style: style) } // MARK: Calendar DataSource and Delegate - class Coordinator: NSObject, CalendarDataSource, CalendarDelegate { - private var view: CalendarDisplayView + final class Coordinator: NSObject, CalendarDataSource, CalendarDelegate { + + private var view: CalendarViewDisplayable + private var eventViewer: EventViewer? var events: [Event] = [] { didSet { + view.events = events view.calendar.reloadData() } } var type: CalendarType = .day { didSet { + guard oldValue != type else { return } view.calendar.set(type: type, date: view.selectDate) view.calendar.reloadData() } @@ -72,26 +86,30 @@ struct CalendarDisplayView: UIViewRepresentable, KVKCalendarSettings { var updatedDate: Date? { didSet { - if let date = updatedDate { + if let date = updatedDate, oldValue != date { + view.calendar.scrollTo(date, animated: true) view.selectDate = date view.calendar.reloadData() } } } - init(_ view: CalendarDisplayView) { + var orientation: UIInterfaceOrientation = .unknown { + didSet { + guard oldValue != orientation else { return } + + let offset = UIApplication.shared.screenOffset + var frame = UIScreen.main.bounds + frame.origin.y = 0 + frame.size.height -= (offset.top + offset.bottom) + frame.size.width -= (offset.left + offset.right) + view.calendar.reloadFrame(frame) + } + } + + init(_ view: CalendarViewDisplayable) { self.view = view super.init() - NotificationCenter.default.addObserver(self, - selector: #selector(changedOerintation), - name: UIDevice.orientationDidChangeNotification, - object: nil) - - DispatchQueue.main.asyncAfter(wallDeadline: .now() + 3) { - self.view.loadEvents(dateFormat: view.style.timeSystem.format) { [weak self] (events) in - self?.view.events = events - } - } } func eventsForCalendar(systemEvents: [EKEvent]) -> [Event] { @@ -103,9 +121,12 @@ struct CalendarDisplayView: UIViewRepresentable, KVKCalendarSettings { } func willDisplayEventViewer(date: Date, frame: CGRect) -> UIView? { - view.eventViewer.frame = frame - view.eventViewer.reloadFrame(frame: frame) - return view.eventViewer + if eventViewer == nil { + eventViewer = EventViewer(frame: frame) + } else { + eventViewer?.frame = frame + } + return eventViewer } func didChangeEvent(_ event: Event, start: Date?, end: Date?) { @@ -115,7 +136,7 @@ struct CalendarDisplayView: UIViewRepresentable, KVKCalendarSettings { } func didChangeViewerFrame(_ frame: CGRect) { - view.eventViewer.reloadFrame(frame: frame) + eventViewer?.reloadFrame(frame: frame) } func didAddNewEvent(_ event: Event, _ date: Date?) { @@ -128,13 +149,19 @@ struct CalendarDisplayView: UIViewRepresentable, KVKCalendarSettings { updatedDate = dates.first ?? Date() } - // MARK: Private + @available(iOS 14.0, *) + func willDisplayEventOptionMenu(_ event: Event, type: CalendarType) -> (menu: UIMenu, customButton: UIButton?)? { + view.handleOptionMenu(type: type) + } - @objc private func changedOerintation() { - var frame = UIScreen.main.bounds - frame.origin.y = 0 - frame.size.height -= (view.topOffset + view.bottomOffset) - view.calendar.reloadFrame(frame) + func didSelectEvent(_ event: Event, type: CalendarType, frame: CGRect?) { + print(type, event) + switch type { + case .day: + eventViewer?.text = event.title.timeline + default: + break + } } } diff --git a/Example/Example For SwiftUI/CalendarViewModel.swift b/Example/Example For SwiftUI/CalendarViewModel.swift new file mode 100644 index 00000000..b8d059ed --- /dev/null +++ b/Example/Example For SwiftUI/CalendarViewModel.swift @@ -0,0 +1,32 @@ +// +// CalendarViewModel.swift +// KVKCalendar +// +// Created by Sergei Kviatkovskii on 11/17/22. +// Copyright © 2022 CocoaPods. All rights reserved. +// + +import Foundation +import KVKCalendar + +final class CalendarViewModel: ObservableObject, KVKCalendarSettings, KVKCalendarDataModel { + + // 🤔👹🍻😬🥸 + // only for example + var events: [Event] = [] + + var style: KVKCalendar.Style { + createCalendarStyle() + } + + func loadEvents(completion: @escaping ([Event]) -> Void) { + DispatchQueue.main.asyncAfter(wallDeadline: .now() + 3) { + self.loadEvents(dateFormat: self.style.timeSystem.format, completion: completion) + } + } + + func addNewEvent() -> Event? { + handleNewEvent(Event(ID: "-1"), date: Date()) + } + +} diff --git a/Example/Example For SwiftUI/ContentView.swift b/Example/Example For SwiftUI/ContentView.swift deleted file mode 100644 index 2fa09625..00000000 --- a/Example/Example For SwiftUI/ContentView.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// ContentView.swift -// KVKCalendar_Example -// -// Created by Sergei Kviatkovskii on 4/16/22. -// Copyright © 2022 CocoaPods. All rights reserved. -// - -import SwiftUI -import KVKCalendar - -@available(iOS 14.0, *) -struct ContentView: View { - @State private var typeCalendar = CalendarType.day - @State private var events: [Event] = [] - @State private var updatedDate: Date? - - var body: some View { - NavigationView { - CalendarDisplayView(events: $events, - type: $typeCalendar, - updatedDate: $updatedDate) - .navigationBarTitle("", displayMode: .inline) - .edgesIgnoringSafeArea(.bottom) - .toolbar { - ToolbarItem(placement: .navigationBarLeading) { - HStack { - Picker("", selection: $typeCalendar, content: { - ForEach(CalendarType.allCases, - content: { type in - Text(type.rawValue.capitalized) - }) - }) - .pickerStyle(SegmentedPickerStyle()) - } - } - ToolbarItem(placement: .navigationBarLeading) { - Button { - updatedDate = Date() - } label: { - Text("Today") - .font(.headline) - .foregroundColor(.red) - } - } - } - } - .navigationViewStyle(StackNavigationViewStyle()) - } -} - -@available(iOS 14.0, *) -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView() - } -} diff --git a/Example/KVKCalendar.xcodeproj/project.pbxproj b/Example/KVKCalendar.xcodeproj/project.pbxproj index fc79e626..3e3ed310 100644 --- a/Example/KVKCalendar.xcodeproj/project.pbxproj +++ b/Example/KVKCalendar.xcodeproj/project.pbxproj @@ -14,9 +14,11 @@ 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; EB7697CCE6961FA0161D3D2B /* Pods_KVKCalendar_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDF73D449FF3DFB1BF1063AD /* Pods_KVKCalendar_Example.framework */; }; - F9005843280AF4B80064E546 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9005842280AF4B80064E546 /* ContentView.swift */; }; + F9005843280AF4B80064E546 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9005842280AF4B80064E546 /* CalendarView.swift */; }; F91321AA21DE6CE60076B714 /* events.json in Resources */ = {isa = PBXBuildFile; fileRef = F91321A921DE6CE60076B714 /* events.json */; }; F91321AC21DF64DF0076B714 /* EventViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91321AB21DF64DF0076B714 /* EventViewer.swift */; }; + F9336C5629264467006D4301 /* CalendarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9336C5529264467006D4301 /* CalendarViewModel.swift */; }; + F9336C5729264467006D4301 /* CalendarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9336C5529264467006D4301 /* CalendarViewModel.swift */; }; F95EFF3A25277B4600EA6748 /* CustomDayCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F95EFF3925277B4600EA6748 /* CustomDayCell.swift */; }; F99D656C280184CF001A6E0A /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F99D656B280184CF001A6E0A /* SceneDelegate.swift */; }; F9E6F6F2281E983D000AEADD /* CalendarViewDisplayable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E6F6F1281E983D000AEADD /* CalendarViewDisplayable.swift */; }; @@ -25,7 +27,7 @@ F9E6F6FC281F0C13000AEADD /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F99D656B280184CF001A6E0A /* SceneDelegate.swift */; }; F9E6F6FD281F0C13000AEADD /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; F9E6F6FE281F0C13000AEADD /* KVKCalendarSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E6F6F3281E9891000AEADD /* KVKCalendarSettings.swift */; }; - F9E6F6FF281F0C13000AEADD /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9005842280AF4B80064E546 /* ContentView.swift */; }; + F9E6F6FF281F0C13000AEADD /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9005842280AF4B80064E546 /* CalendarView.swift */; }; F9E6F700281F0C13000AEADD /* EventViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91321AB21DF64DF0076B714 /* EventViewer.swift */; }; F9E6F701281F0C13000AEADD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; F9E6F702281F0C13000AEADD /* CustomDayCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F95EFF3925277B4600EA6748 /* CustomDayCell.swift */; }; @@ -64,9 +66,10 @@ BBA5555F4434EF626F6DCFF0 /* Pods-KVKCalendar_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KVKCalendar_Tests.release.xcconfig"; path = "Target Support Files/Pods-KVKCalendar_Tests/Pods-KVKCalendar_Tests.release.xcconfig"; sourceTree = ""; }; C64C5D6AC10D0094FEEF80DC /* Pods-KVKCalendar_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KVKCalendar_Example.release.xcconfig"; path = "Target Support Files/Pods-KVKCalendar_Example/Pods-KVKCalendar_Example.release.xcconfig"; sourceTree = ""; }; CDF73D449FF3DFB1BF1063AD /* Pods_KVKCalendar_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_KVKCalendar_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F9005842280AF4B80064E546 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + F9005842280AF4B80064E546 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = ""; }; F91321A921DE6CE60076B714 /* events.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = events.json; sourceTree = ""; }; F91321AB21DF64DF0076B714 /* EventViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventViewer.swift; sourceTree = ""; }; + F9336C5529264467006D4301 /* CalendarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarViewModel.swift; sourceTree = ""; }; F95EFF3925277B4600EA6748 /* CustomDayCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDayCell.swift; sourceTree = ""; }; F99D656B280184CF001A6E0A /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; F9E6F6F1281E983D000AEADD /* CalendarViewDisplayable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarViewDisplayable.swift; sourceTree = ""; }; @@ -205,7 +208,8 @@ F9005841280AF4750064E546 /* Example For SwiftUI */ = { isa = PBXGroup; children = ( - F9005842280AF4B80064E546 /* ContentView.swift */, + F9005842280AF4B80064E546 /* CalendarView.swift */, + F9336C5529264467006D4301 /* CalendarViewModel.swift */, F9E6F6F1281E983D000AEADD /* CalendarViewDisplayable.swift */, ); path = "Example For SwiftUI"; @@ -292,6 +296,9 @@ LastSwiftMigration = 1020; TestTargetID = 607FACCF1AFB9204008FA782; }; + F9E6F6F8281F0C13000AEADD = { + DevelopmentTeam = H2NQL66JNC; + }; }; }; buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "KVKCalendar" */; @@ -492,8 +499,9 @@ F99D656C280184CF001A6E0A /* SceneDelegate.swift in Sources */, 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, F9E6F6F4281E9891000AEADD /* KVKCalendarSettings.swift in Sources */, - F9005843280AF4B80064E546 /* ContentView.swift in Sources */, + F9005843280AF4B80064E546 /* CalendarView.swift in Sources */, F91321AC21DF64DF0076B714 /* EventViewer.swift in Sources */, + F9336C5629264467006D4301 /* CalendarViewModel.swift in Sources */, 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, F95EFF3A25277B4600EA6748 /* CustomDayCell.swift in Sources */, ); @@ -515,8 +523,9 @@ F9E6F6FC281F0C13000AEADD /* SceneDelegate.swift in Sources */, F9E6F6FD281F0C13000AEADD /* ViewController.swift in Sources */, F9E6F6FE281F0C13000AEADD /* KVKCalendarSettings.swift in Sources */, - F9E6F6FF281F0C13000AEADD /* ContentView.swift in Sources */, + F9E6F6FF281F0C13000AEADD /* CalendarView.swift in Sources */, F9E6F700281F0C13000AEADD /* EventViewer.swift in Sources */, + F9336C5729264467006D4301 /* CalendarViewModel.swift in Sources */, F9E6F701281F0C13000AEADD /* AppDelegate.swift in Sources */, F9E6F702281F0C13000AEADD /* CustomDayCell.swift in Sources */, ); @@ -593,7 +602,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -644,7 +653,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -662,7 +671,7 @@ DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = KVKCalendar/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; @@ -686,7 +695,7 @@ DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = KVKCalendar/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; @@ -747,9 +756,9 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = KVKCalendar_Example.entitlements; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = H2NQL66JNC; INFOPLIST_FILE = "$(SRCROOT)/KVKCalendar_Example_SwiftUI-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; @@ -771,9 +780,9 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = KVKCalendar_Example.entitlements; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = H2NQL66JNC; INFOPLIST_FILE = "$(SRCROOT)/KVKCalendar_Example_SwiftUI-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; diff --git a/Example/KVKCalendar.xcodeproj/xcshareddata/xcschemes/KVKCalendar-Example.xcscheme b/Example/KVKCalendar.xcodeproj/xcshareddata/xcschemes/KVKCalendar-Example.xcscheme index a8c87785..1ffd845d 100644 --- a/Example/KVKCalendar.xcodeproj/xcshareddata/xcschemes/KVKCalendar-Example.xcscheme +++ b/Example/KVKCalendar.xcodeproj/xcshareddata/xcschemes/KVKCalendar-Example.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> @@ -58,6 +58,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + region = "US" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" @@ -74,6 +75,10 @@ ReferencedContainer = "container:KVKCalendar.xcodeproj"> + + + version = "1.7"> @@ -34,6 +34,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "en" + region = "US" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" @@ -50,6 +52,10 @@ ReferencedContainer = "container:KVKCalendar.xcodeproj"> + + UISceneConfiguration { diff --git a/Example/KVKCalendar/Base.lproj/LaunchScreen.xib b/Example/KVKCalendar/Base.lproj/LaunchScreen.xib index 120e808e..600b75dc 100644 --- a/Example/KVKCalendar/Base.lproj/LaunchScreen.xib +++ b/Example/KVKCalendar/Base.lproj/LaunchScreen.xib @@ -1,12 +1,10 @@ - - - - + + - - + + @@ -17,19 +15,17 @@ - + @@ -43,4 +39,9 @@ + + + + + diff --git a/Example/KVKCalendar/EventViewer.swift b/Example/KVKCalendar/EventViewer.swift index e986741d..583897d8 100644 --- a/Example/KVKCalendar/EventViewer.swift +++ b/Example/KVKCalendar/EventViewer.swift @@ -20,11 +20,7 @@ final class EventViewer: UIView { private let lineView: UIView = { let view = UIView() - if #available(iOS 13.0, *) { - view.backgroundColor = .systemGray3 - } else { - view.backgroundColor = .lightGray - } + view.backgroundColor = .systemGray3 return view }() @@ -36,11 +32,8 @@ final class EventViewer: UIView { override init(frame: CGRect) { super.init(frame: frame) - if #available(iOS 13.0, *) { - backgroundColor = .systemBackground - } else { - backgroundColor = .white - } + backgroundColor = .systemBackground + reloadFrame(frame: CGRect(origin: .zero, size: frame.size)) addSubview(textLabel) addSubview(lineView) } diff --git a/Example/KVKCalendar/KVKCalendarSettings.swift b/Example/KVKCalendar/KVKCalendarSettings.swift index d668dda3..db59140a 100644 --- a/Example/KVKCalendar/KVKCalendarSettings.swift +++ b/Example/KVKCalendar/KVKCalendarSettings.swift @@ -10,47 +10,16 @@ import Foundation import KVKCalendar import EventKit -protocol KVKCalendarSettings { +protocol KVKCalendarDataModel { - var selectDate: Date { get set } var events: [Event] { get set } var style: Style { get } - var eventViewer: EventViewer { get set } } -extension KVKCalendarSettings { - - var topOffset: CGFloat { - let barHeight = UIApplication.shared.statusBarHeight - if #available(iOS 11.0, *) { - return UIApplication.shared.activeWindow?.rootViewController?.view.safeAreaInsets.top ?? barHeight - } else { - return barHeight - } - } - - var bottomOffset: CGFloat { - if #available(iOS 11.0, *) { - return UIApplication.shared.activeWindow?.rootViewController?.view.safeAreaInsets.bottom ?? 0 - } else { - return 0 - } - } - - var defaultStringDate: String { - "14.12.2022" - } - - var defaultDate: Date { - onlyDateFormatter.date(from: defaultStringDate) ?? Date() - } - - var onlyDateFormatter: DateFormatter { - let formatter = DateFormatter() - formatter.dateFormat = "dd.MM.yyyy" - return formatter - } +protocol KVKCalendarSettings {} + +extension KVKCalendarSettings where Self: KVKCalendarDataModel { func handleChangingEvent(_ event: Event, start: Date?, end: Date?) -> (range: Range, events: [Event])? { var eventTemp = event @@ -84,23 +53,6 @@ extension KVKCalendarSettings { } } - func handleCustomEventView(event: Event, style: Style, frame: CGRect) -> EventViewGeneral? { - guard event.ID == "2" else { return nil } - - return CustomViewEvent(style: style, event: event, frame: frame) - } - - @available(iOS 13.0, *) - func handleOptionMenu(type: CalendarType) -> (menu: UIMenu, customButton: UIButton?)? { - guard type == .day else { return nil } - - let action = UIAction(title: "Test", attributes: .destructive) { _ in - print("test tap") - } - - return (UIMenu(title: "Test menu", children: [action]), nil) - } - func handleNewEvent(_ event: Event, date: Date?) -> Event? { var newEvent = event @@ -118,28 +70,6 @@ extension KVKCalendarSettings { return newEvent } - func handleCell(parameter: CellParameter, - type: CalendarType, - view: T, - indexPath: IndexPath) -> KVKCalendarCellProtocol? where T: UIScrollView { - switch type { - case .year where parameter.date?.kvkMonth == Date().kvkMonth: - let cell = (view as? UICollectionView)?.kvkDequeueCell(indexPath: indexPath) { (cell: CustomDayCell) in - cell.imageView.image = UIImage(named: "ic_stub") - } - return cell - case .day, .week, .month: - guard parameter.date?.kvkDay == Date().kvkDay && parameter.type != .empty else { return nil } - - let cell = (view as? UICollectionView)?.kvkDequeueCell(indexPath: indexPath) { (cell: CustomDayCell) in - cell.imageView.image = UIImage(named: "ic_stub") - } - return cell - default: - return nil - } - } - func handleEvents(systemEvents: [EKEvent]) -> [Event] { // if you want to get a system events, you need to set style.systemCalendars = ["test"] let mappedEvents = systemEvents.compactMap { (event) -> Event in @@ -153,31 +83,6 @@ extension KVKCalendarSettings { return events + mappedEvents } - func createCalendarStyle() -> Style { - var style = Style() - style.timeline.isHiddenStubEvent = false - style.startWeekDay = .sunday - style.systemCalendars = ["Calendar1", "Calendar2", "Calendar3"] - if #available(iOS 13.0, *) { - style.event.iconFile = UIImage(systemName: "paperclip") - } - style.timeline.scrollLineHourMode = .onlyOnInitForDate(defaultDate) - style.timeline.showLineHourMode = .always - return style - } - - func timeFormatter(date: Date, format: String) -> String { - let formatter = DateFormatter() - formatter.dateFormat = format - return formatter.string(from: date) - } - - func formatter(date: String) -> Date { - let formatter = DateFormatter() - formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" - return formatter.date(from: date) ?? Date() - } - func loadEvents(dateFormat: String, completion: ([Event]) -> Void) { let decoder = JSONDecoder() @@ -213,9 +118,12 @@ extension KVKCalendarSettings { var customeStyle = style.event customeStyle.defaultHeight = 40 event.style = customeStyle - } - if item.id == "40" { + } else if item.id == "40" { event.recurringType = .everyYear + } else if item.id == "1400" { + var customeStyle = style.event + customeStyle.defaultWidth = 40 + event.style = customeStyle } return event }) @@ -224,6 +132,92 @@ extension KVKCalendarSettings { } +extension KVKCalendarSettings { + + var defaultStringDate: String { + "14.11.2022" + } + + var defaultDate: Date { + onlyDateFormatter.date(from: defaultStringDate) ?? Date() + } + + var onlyDateFormatter: DateFormatter { + let formatter = DateFormatter() + formatter.dateFormat = "dd.MM.yyyy" + return formatter + } + + func handleCustomEventView(event: Event, style: Style, frame: CGRect) -> EventViewGeneral? { + switch event.ID { + case "2": + return CustomViewEvent(style: style, event: event, frame: frame) + case "1400": + return BlockViewEvent(style: style, event: event, frame: frame) + default: + return nil + } + } + + func handleOptionMenu(type: CalendarType) -> (menu: UIMenu, customButton: UIButton?)? { + guard type == .day else { return nil } + + let action = UIAction(title: "Delete", attributes: .destructive) { _ in + print("test tap") + } + + return (UIMenu(title: "Options", children: [action]), nil) + } + + func handleCell(parameter: CellParameter, + type: CalendarType, + view: T, + indexPath: IndexPath) -> KVKCalendarCellProtocol? where T: UIScrollView { + switch type { + case .year where parameter.date?.kvkMonth == Date().kvkMonth: + let cell = (view as? UICollectionView)?.kvkDequeueCell(indexPath: indexPath) { (cell: CustomDayCell) in + cell.imageView.image = UIImage(named: "ic_stub") + } + return cell + case .day, .week, .month: + guard parameter.date?.kvkDay == Date().kvkDay && parameter.type != .empty else { return nil } + + let cell = (view as? UICollectionView)?.kvkDequeueCell(indexPath: indexPath) { (cell: CustomDayCell) in + cell.imageView.image = UIImage(named: "ic_stub") + } + return cell + default: + return nil + } + } + + func createCalendarStyle() -> Style { + var style = Style() + style.timeline.isHiddenStubEvent = false + style.startWeekDay = .sunday + style.systemCalendars = ["Calendar1", "Calendar2", "Calendar3"] + style.event.iconFile = UIImage(systemName: "paperclip") + style.timeline.scrollLineHourMode = .onlyOnInitForDate(defaultDate) + style.timeline.showLineHourMode = .always + style.month.autoSelectionDateWhenScrolling = true + style.timeline.useDefaultCorderHeader = true + return style + } + + func timeFormatter(date: Date, format: String) -> String { + let formatter = DateFormatter() + formatter.dateFormat = format + return formatter.string(from: date) + } + + func formatter(date: String) -> Date { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" + return formatter.date(from: date) ?? Date() + } + +} + final class CustomViewEvent: EventViewGeneral { override init(style: Style, event: Event, frame: CGRect) { super.init(style: style, event: event, frame: frame) @@ -232,7 +226,6 @@ final class CustomViewEvent: EventViewGeneral { imageView.frame = CGRect(origin: CGPoint(x: 3, y: 1), size: CGSize(width: frame.width - 6, height: frame.height - 2)) imageView.contentMode = .scaleAspectFit addSubview(imageView) - backgroundColor = event.backgroundColor } required init?(coder: NSCoder) { @@ -240,6 +233,24 @@ final class CustomViewEvent: EventViewGeneral { } } +final class BlockViewEvent: EventViewGeneral { + override init(style: Style, event: Event, frame: CGRect) { + var updatedStyle = style + updatedStyle.event.states = [] + super.init(style: updatedStyle, event: event, frame: frame) + isUserInteractionEnabled = false + backgroundColor = event.color?.value + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func draw(_ rect: CGRect) { + + } +} + struct ItemData: Decodable { let data: [Item] @@ -310,11 +321,19 @@ extension UIApplication { } var statusBarHeight: CGFloat { - if #available(iOS 13.0, *) { - return activeWindow?.windowScene?.statusBarManager?.statusBarFrame.height ?? 24 - } else { - return statusBarFrame.height + activeWindow?.windowScene?.statusBarManager?.statusBarFrame.height ?? 24 + } + + var windowSize: CGSize { + windows.last?.bounds.size ?? UIScreen.main.bounds.size + } + + var screenOffset: UIEdgeInsets { + var oldInsets: UIEdgeInsets { + let barHeight = UIApplication.shared.statusBarHeight + return UIEdgeInsets(top: barHeight, left: 0, bottom: 0, right: 0) } + return UIApplication.shared.activeWindow?.rootViewController?.view.safeAreaInsets ?? oldInsets } } diff --git a/Example/KVKCalendar/SceneDelegate.swift b/Example/KVKCalendar/SceneDelegate.swift index 2aad56b3..a67c1723 100644 --- a/Example/KVKCalendar/SceneDelegate.swift +++ b/Example/KVKCalendar/SceneDelegate.swift @@ -8,7 +8,6 @@ import UIKit import SwiftUI -@available(iOS 13.0, *) class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? @@ -20,30 +19,34 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: windowScene) - let navVC = UINavigationController(rootViewController: makeViewController()) - navVC.isNavigationBarHidden = false - navVC.navigationBar.isTranslucent = false - - if #available(iOS 15.0, *) { - let appearance = UINavigationBarAppearance() - appearance.configureWithOpaqueBackground() - navVC.navigationBar.standardAppearance = appearance - navVC.navigationBar.scrollEdgeAppearance = appearance - } - - window?.rootViewController = navVC + let vc = makeViewController() + window?.rootViewController = vc window?.makeKeyAndVisible() } private func makeViewController() -> UIViewController { + func createNavVC(_ vc: UIViewController) -> UINavigationController { + let navVC = UINavigationController(rootViewController: vc) + navVC.isNavigationBarHidden = false + navVC.navigationBar.isTranslucent = false + + if #available(iOS 15.0, *) { + let appearance = UINavigationBarAppearance() + appearance.configureWithOpaqueBackground() + navVC.navigationBar.standardAppearance = appearance + navVC.navigationBar.scrollEdgeAppearance = appearance + } + return navVC + } + if #available(iOS 14.0, *) { #if DEBUG_SwiftUI - return UIHostingController(rootView: ContentView()) + return UIHostingController(rootView: CalendarView()) #else - return ViewController() + return createNavVC(ViewController()) #endif } else { - return ViewController() + return createNavVC(ViewController()) } } diff --git a/Example/KVKCalendar/ViewController.swift b/Example/KVKCalendar/ViewController.swift index 35e33de4..999f1c27 100644 --- a/Example/KVKCalendar/ViewController.swift +++ b/Example/KVKCalendar/ViewController.swift @@ -10,7 +10,7 @@ import UIKit import KVKCalendar import EventKit -final class ViewController: UIViewController, KVKCalendarSettings, UIPopoverPresentationControllerDelegate { +final class ViewController: UIViewController, KVKCalendarSettings, KVKCalendarDataModel, UIPopoverPresentationControllerDelegate { var events = [Event]() { didSet { @@ -35,24 +35,26 @@ final class ViewController: UIViewController, KVKCalendarSettings, UIPopoverPres return button }() - private lazy var calendarView: CalendarView = { + private lazy var calendarView: KVKCalendarView = { var frame = view.frame frame.origin.y = 0 - let calendar = CalendarView(frame: frame, date: selectDate, style: style) + let calendar = KVKCalendarView(frame: frame, date: selectDate, style: style) calendar.delegate = self calendar.dataSource = self return calendar }() - private lazy var segmentedControl: UISegmentedControl = { - let array = CalendarType.allCases - let control = UISegmentedControl(items: array.map { $0.rawValue.capitalized }) - control.tintColor = .systemRed - control.selectedSegmentIndex = 0 - control.addTarget(self, action: #selector(switchCalendar), for: .valueChanged) - return control - }() - + private var calendarTypeBtn: UIBarButtonItem { + if #available(iOS 14.0, *) { + let btn = UIBarButtonItem(title: calendarView.selectedType.title, menu: createCalendarTypesMenu()) + btn.style = .done + btn.tintColor = .systemRed + return btn + } else { + return UIBarButtonItem() + } + } + init() { super.init(nibName: nil, bundle: nil) selectDate = defaultDate @@ -64,15 +66,10 @@ final class ViewController: UIViewController, KVKCalendarSettings, UIPopoverPres override func viewDidLoad() { super.viewDidLoad() - - if #available(iOS 13.0, *) { - view.backgroundColor = .systemBackground - } else { - view.backgroundColor = .white - } + navigationItem.title = "KVKCalendar" + view.backgroundColor = .systemBackground view.addSubview(calendarView) - navigationItem.titleView = segmentedControl - navigationItem.rightBarButtonItems = [todayButton, reloadStyle] + setupBarButtons() loadEvents(dateFormat: style.timeSystem.format) { (events) in DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in @@ -101,10 +98,22 @@ final class ViewController: UIViewController, KVKCalendarSettings, UIPopoverPres calendarView.reloadData() } - @objc private func switchCalendar(sender: UISegmentedControl) { - let type = CalendarType.allCases[sender.selectedSegmentIndex] - calendarView.set(type: type, date: selectDate) - calendarView.reloadData() + private func setupBarButtons() { + navigationItem.leftBarButtonItems = [calendarTypeBtn, todayButton] + navigationItem.rightBarButtonItems = [reloadStyle] + } + + @available(iOS 14.0, *) + private func createCalendarTypesMenu() -> UIMenu { + let actions: [UIMenuElement] = KVKCalendar.CalendarType.allCases.compactMap { (item) in + UIAction(title: item.title, state: item == calendarView.selectedType ? .on : .off) { [weak self] (_) in + guard let self = self else { return } + self.calendarView.set(type: item, date: self.selectDate) + self.calendarView.reloadData() + self.setupBarButtons() + } + } + return UIMenu(children: actions) } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { @@ -117,6 +126,7 @@ final class ViewController: UIViewController, KVKCalendarSettings, UIPopoverPres self?.events = events } } + } // MARK: - Calendar delegate @@ -166,13 +176,8 @@ extension ViewController: CalendarDelegate { extension ViewController: CalendarDataSource { - func dequeueAllDayViewEvent(_ event: Event, date: Date, frame: CGRect) -> UIView? { - if date.kvkDay == 11 { - let view = UIView(frame: frame) - view.backgroundColor = .systemRed - return view - } - return nil + func willSelectDate(_ date: Date, type: CalendarType) { + print(date, type) } @available(iOS 14.0, *) diff --git a/Example/KVKCalendar/events.json b/Example/KVKCalendar/events.json index 4911fa28..6b2f706a 100644 --- a/Example/KVKCalendar/events.json +++ b/Example/KVKCalendar/events.json @@ -1,12 +1,232 @@ { "data": [ { - "all_day": 1, + "all_day": 0, + "border_color": "#000000", + "color": "#ff0000", + "end": "2022-11-14T09:00:00+01:00", + "id": "1401", + "start": "2022-11-14T08:00:00+01:00", + "text_color": "#0000ff", + "title": "Büro", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#0000ff", + "end": "2022-11-14T14:00:00+01:00", + "id": "1400", + "start": "2022-11-14T08:00:00+01:00", + "text_color": "#000000", + "title": "JS", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#0000ff", + "end": "2022-11-14T10:00:00+01:00", + "id": "1402", + "start": "2022-11-14T09:00:00+01:00", + "text_color": "#0000ff", + "title": "1 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#0000ff", + "end": "2022-11-14T11:00:00+01:00", + "id": "1403", + "start": "2022-11-14T10:00:00+01:00", + "text_color": "#0000ff", + "title": "2 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#0000ff", + "end": "2022-11-14T12:00:00+01:00", + "id": "1404", + "start": "2022-11-14T11:00:00+01:00", + "text_color": "#0000ff", + "title": "3 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#0000ff", + "end": "2022-11-14T12:00:00+01:00", + "id": "1405", + "start": "2022-11-14T11:00:00+01:00", + "text_color": "#0000ff", + "title": "4 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#0000ff", + "end": "2022-11-14T12:00:00+01:00", + "id": "1406", + "start": "2022-11-14T11:00:00+01:00", + "text_color": "#0000ff", + "title": "5 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#003399", + "end": "2022-11-14T13:00:00+01:00", + "id": "1407", + "start": "2022-11-14T12:00:00+01:00", + "text_color": "#0000ff", + "title": "6 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#003399", + "end": "2022-11-14T13:00:00+01:00", + "id": "1408", + "start": "2022-11-14T12:00:00+01:00", + "text_color": "0000ff", + "title": "7 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#ff0000", + "end": "2022-11-14T14:00:00+01:00", + "id": "1409", + "start": "2022-11-14T13:00:00+01:00", + "text_color": "0000ff", + "title": "8 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#0000ff", + "end": "2022-11-14T14:00:00+01:00", + "id": "1410", + "start": "2022-11-14T13:00:00+01:00", + "text_color": "0000ff", + "title": "10 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#808080", + "end": "2022-11-14T20:00:00+01:00", + "id": "1412", + "start": "2022-11-14T14:00:00+01:00", + "text_color": "0000ff", + "title": "TS", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#0000ff", + "end": "2022-11-14T14:00:00+01:00", + "id": "1411", + "start": "2022-11-14T13:00:00+01:00", + "text_color": "0000ff", + "title": "101 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#0000ff", + "end": "2022-11-14T15:00:00+01:00", + "id": "1413", + "start": "2022-11-14T14:00:00+01:00", + "text_color": "0000ff", + "title": "11 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#0000ff", + "end": "2022-11-14T18:00:00+01:00", + "id": "1414", + "start": "2022-11-14T15:00:00+01:00", + "text_color": "0000ff", + "title": "JS", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#0000ff", + "end": "2022-11-14T16:00:00+01:00", + "id": "1415", + "start": "2022-11-14T15:00:00+01:00", + "text_color": "0000ff", + "title": "13 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#0000ff", + "end": "2022-11-14T17:00:00+01:00", + "id": "14161", + "start": "2022-11-14T16:00:00+01:00", + "text_color": "0000ff", + "title": "13 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#808080", + "end": "2022-11-14T18:00:00+01:00", + "id": "1416", + "start": "2022-11-14T17:00:00+01:00", + "text_color": "0000ff", + "title": "14 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#808080", + "end": "2022-11-14T19:00:00+01:00", + "id": "1417", + "start": "2022-11-14T18:00:00+01:00", + "text_color": "0000ff", + "title": "14 Test", + "files": [] + }, + { + "all_day": 0, + "border_color": "#000000", + "color": "#808080", + "end": "2022-11-14T20:00:00+01:00", + "id": "1419", + "start": "2022-11-14T19:00:00+01:00", + "text_color": "0000ff", + "title": "14 Test", + "files": [] + }, + { + "all_day": 0, "border_color": "#FFFFFF", "color": "#12c795", - "end": "2022-12-10T09:00:00+03:00", + "end": "2022-12-11T09:00:00+03:00", "id": "40", - "start": "2022-12-10T08:30:00+03:00", + "start": "2022-12-11T08:30:00+03:00", "text_color": "#000000", "title": "All day event number 1", "files": [] @@ -180,9 +400,9 @@ "all_day": 0, "border_color": "#FFFFFF", "color": "#ed6160", - "end": "2022-12-10T14:30:00+03:00", + "end": "2022-12-11T14:30:00+03:00", "id": "2", - "start": "2022-12-10T13:00:00+03:00", + "start": "2022-12-11T13:00:00+03:00", "text_color": "#000000", "title": "Event number 4", "files": ["file"] @@ -191,9 +411,9 @@ "all_day": 0, "border_color": "#FFFFFF", "color": "#3f645a", - "end": "2022-12-10T09:50:00+03:00", + "end": "2022-12-11T09:50:00+03:00", "id": "3", - "start": "2022-12-10T09:00:00+03:00", + "start": "2022-12-11T09:00:00+03:00", "text_color": "#000000", "title": "Event number 5", "files": ["file"] @@ -202,9 +422,9 @@ "all_day": 0, "border_color": "#FFFFFF", "color": "#93c47d", - "end": "2022-12-10T09:40:00+03:00", + "end": "2022-12-11T09:40:00+03:00", "id": "4", - "start": "2022-12-10T09:10:00+03:00", + "start": "2022-12-11T09:10:00+03:00", "text_color": "#000000", "title": "Event number 6", "files": ["file"] @@ -214,8 +434,8 @@ "id": "5", "border_color": "#FFFFFF", "color": "#007181", - "end": "2022-12-10T10:00:00+03:00", - "start": "2022-12-10T09:30:00+03:00", + "end": "2022-12-11T10:00:00+03:00", + "start": "2022-12-11T09:30:00+03:00", "text_color": "#000000", "title": "Event number 7", "files": [] @@ -236,8 +456,8 @@ "id": "62", "border_color": "#FFFFFF", "color": "#662f63", - "end": "2022-12-10T12:00:00+03:00", - "start": "2022-12-10T09:25:00+03:00", + "end": "2022-12-11T12:00:00+03:00", + "start": "2022-12-11T09:25:00+03:00", "text_color": "#000000", "title": "New 1", "files": [] @@ -258,8 +478,8 @@ "id": "64", "border_color": "#FFFFFF", "color": "#662f63", - "end": "2022-12-10T09:50:00+03:00", - "start": "2022-12-10T09:05:00+03:00", + "end": "2022-12-11T09:50:00+03:00", + "start": "2022-12-11T09:05:00+03:00", "text_color": "#000000", "title": "New 3", "files": [] @@ -302,8 +522,8 @@ "id": "8", "border_color": "#FFFFFF", "color": "#ff4040", - "end": "2022-12-10T11:00:00+03:00", - "start": "2022-12-10T10:30:00+03:00", + "end": "2022-12-11T11:30:00+03:00", + "start": "2022-12-11T11:00:00+03:00", "text_color": "#000000", "title": "Event number 12", "files": [] @@ -368,8 +588,8 @@ "id": "14", "border_color": "#FFFFFF", "color": "#12c795", - "end": "2022-12-14T17:00:00+03:00", - "start": "2022-12-14T13:30:00+03:00", + "end": "2022-11-30T17:00:00+03:00", + "start": "2022-11-30T13:30:00+03:00", "text_color": "#000000", "title": "Event number 18", "files": [] @@ -379,8 +599,8 @@ "id": "15", "border_color": "#FFFFFF", "color": "#09ce65", - "end": "2022-12-10T16:00:00+03:00", - "start": "2022-12-10T14:00:00+03:00", + "end": "2022-12-11T16:00:00+03:00", + "start": "2022-12-11T14:00:00+03:00", "text_color": "#000000", "title": "Event number 19", "files": [] diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index 92e91907..970320ff 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -15,7 +15,7 @@ 0FB542A0D7F7E13C96F374C62F1120B3 /* YearHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD583DD1362817FC0C076C2BD05EFFEF /* YearHeaderView.swift */; }; 150BDB4BC8B70F6119C80D54D6FD0347 /* ListViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80A6E712A274C8991D5F26EA2E34A59 /* ListViewCell.swift */; }; 17B97139898B1CE05B2505DBC30FFC65 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D3A26A4581F2BD9FBA6548A2CF518A5 /* TimelineView.swift */; }; - 17BEAAC59928F37A39C7E34B5D385C0B /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC1EFA39EACC49AC87CDD4DC127B85B /* CalendarView.swift */; }; + 17BEAAC59928F37A39C7E34B5D385C0B /* KVKCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC1EFA39EACC49AC87CDD4DC127B85B /* KVKCalendarView.swift */; }; 1854C4D2AAD2F8BFB7D5D885FC47C049 /* Timeline+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E83C36C28E583BC72A6335399BA7D538 /* Timeline+Extension.swift */; }; 18E5875BD7D392E12CFD936351479DAE /* Style.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33229F7143326A36E5454B4CD8D88959 /* Style.swift */; }; 238A1F9D92A410F08F3775423AEDC423 /* WeekView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25C43A385CAEAB40679E977A7DD01D1F /* WeekView.swift */; }; @@ -35,7 +35,7 @@ 67FF774F81829D25627A95523D121DCB /* AllDayEventModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 778DFD3FBB832D6E4D23F25335FFBEF0 /* AllDayEventModel.swift */; }; 681E095B5B26C2C43F3712DE60C0EE04 /* KVKCalendar-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4156390BBF62FCCDDD0E7939667888E7 /* KVKCalendar-dummy.m */; }; 6F0D7E0034E08D9E007E126E021CEAFB /* MonthCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96098C6CF6C1BA70E1197BA7471FC51F /* MonthCell.swift */; }; - 71F3B422C4D8488E7E4ADD2E2A52C94D /* CalendarView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95889043D19F567D2F068D3DF793D13 /* CalendarView+Extension.swift */; }; + 71F3B422C4D8488E7E4ADD2E2A52C94D /* KVKCalendarView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95889043D19F567D2F068D3DF793D13 /* KVKCalendarView+Extension.swift */; }; 7AB5F896D779959653E7AD5286432B46 /* EventKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB364C8715A22990F1D7C8B6FDE7F94F /* EventKit.framework */; }; 7B2B45BE5BD852E6BC3AAA9621F088CF /* DayPadCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B6179D20EE3F4E1B968A8E83351B79D /* DayPadCell.swift */; }; 7E5134C9C2E7CDE8C290E0F8640157A1 /* MonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 313894D26574F72BECD3DCB3780EC5D7 /* MonthData.swift */; }; @@ -70,6 +70,7 @@ F9535B33272DE8CC008AAC56 /* KVKTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9535B32272DE8CC008AAC56 /* KVKTableViewCell.swift */; }; F9535B35272DE9F5008AAC56 /* KVKTableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9535B34272DE9F5008AAC56 /* KVKTableViewHeaderFooterView.swift */; }; F969448826F3534500EF4929 /* KVKCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F969448726F3534500EF4929 /* KVKCollectionViewCell.swift */; }; + F98F027F2924C38500416B9A /* SwiftUI+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F98F027E2924C38500416B9A /* SwiftUI+Extensions.swift */; }; F9C481BB26A60F420007A780 /* SkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C481BA26A60F420007A780 /* SkeletonView.swift */; }; F9D21F7C27FB787D00B1E6D1 /* ScrollableWeekView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9D21F7B27FB787D00B1E6D1 /* ScrollableWeekView.swift */; }; /* End PBXBuildFile section */ @@ -93,7 +94,7 @@ /* Begin PBXFileReference section */ 05D39769BF1FB04597237FDE1A915CED /* KVKCalendar.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = KVKCalendar.release.xcconfig; sourceTree = ""; }; - 0AC1EFA39EACC49AC87CDD4DC127B85B /* CalendarView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CalendarView.swift; path = Sources/KVKCalendar/CalendarView.swift; sourceTree = ""; }; + 0AC1EFA39EACC49AC87CDD4DC127B85B /* KVKCalendarView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KVKCalendarView.swift; path = Sources/KVKCalendar/KVKCalendarView.swift; sourceTree = ""; }; 0D5C6CD6E167ED79AFEAB813B7295C15 /* Pods-KVKCalendar_Example-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-KVKCalendar_Example-acknowledgements.markdown"; sourceTree = ""; }; 0EBA107C32E7332EC64016619D0A9E5A /* Pods-KVKCalendar_Example-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-KVKCalendar_Example-acknowledgements.plist"; sourceTree = ""; }; 101E3B4C5CDBC2588321448AF74769C4 /* KVKCalendar.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = KVKCalendar.modulemap; sourceTree = ""; }; @@ -142,7 +143,7 @@ 9DDDFB44C1E4A1CAC17F5959155FCA80 /* CalendarModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CalendarModel.swift; path = Sources/KVKCalendar/CalendarModel.swift; sourceTree = ""; }; A041F8C0C72AA971369C6848FF758D74 /* Pods_KVKCalendar_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_KVKCalendar_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A09F113E4975793648F12FDC38102325 /* ResizeEventView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResizeEventView.swift; path = Sources/KVKCalendar/ResizeEventView.swift; sourceTree = ""; }; - A95889043D19F567D2F068D3DF793D13 /* CalendarView+Extension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "CalendarView+Extension.swift"; path = "Sources/KVKCalendar/CalendarView+Extension.swift"; sourceTree = ""; }; + A95889043D19F567D2F068D3DF793D13 /* KVKCalendarView+Extension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "KVKCalendarView+Extension.swift"; path = "Sources/KVKCalendar/KVKCalendarView+Extension.swift"; sourceTree = ""; }; A9624963DF6186D58BC646C877E68CDF /* Pods-KVKCalendar_Tests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-KVKCalendar_Tests-dummy.m"; sourceTree = ""; }; AB364C8715A22990F1D7C8B6FDE7F94F /* EventKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EventKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/EventKit.framework; sourceTree = DEVELOPER_DIR; }; AD144A2432F96A11EBF7681230A39D4F /* DayPhoneCell.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DayPhoneCell.swift; path = Sources/KVKCalendar/DayPhoneCell.swift; sourceTree = ""; }; @@ -177,6 +178,7 @@ F9535B32272DE8CC008AAC56 /* KVKTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = KVKTableViewCell.swift; path = Sources/KVKCalendar/KVKTableViewCell.swift; sourceTree = ""; }; F9535B34272DE9F5008AAC56 /* KVKTableViewHeaderFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = KVKTableViewHeaderFooterView.swift; path = Sources/KVKCalendar/KVKTableViewHeaderFooterView.swift; sourceTree = ""; }; F969448726F3534500EF4929 /* KVKCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = KVKCollectionViewCell.swift; path = Sources/KVKCalendar/KVKCollectionViewCell.swift; sourceTree = ""; }; + F98F027E2924C38500416B9A /* SwiftUI+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "SwiftUI+Extensions.swift"; path = "Sources/KVKCalendar/SwiftUI+Extensions.swift"; sourceTree = ""; }; F9A5FE2DD23FBAFCC63D57A554B1036E /* Pods-KVKCalendar_Example-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-KVKCalendar_Example-frameworks.sh"; sourceTree = ""; }; F9C481BA26A60F420007A780 /* SkeletonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SkeletonView.swift; path = Sources/KVKCalendar/SkeletonView.swift; sourceTree = ""; }; F9D21F7B27FB787D00B1E6D1 /* ScrollableWeekView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ScrollableWeekView.swift; path = Sources/KVKCalendar/ScrollableWeekView.swift; sourceTree = ""; }; @@ -297,8 +299,8 @@ DCBFA56F9C790AF87352E834A0D6D95D /* Calendar+Extension.swift */, 8A296C8DC2AC32446F7C661A876C043A /* CalendarData.swift */, 9DDDFB44C1E4A1CAC17F5959155FCA80 /* CalendarModel.swift */, - 0AC1EFA39EACC49AC87CDD4DC127B85B /* CalendarView.swift */, - A95889043D19F567D2F068D3DF793D13 /* CalendarView+Extension.swift */, + 0AC1EFA39EACC49AC87CDD4DC127B85B /* KVKCalendarView.swift */, + A95889043D19F567D2F068D3DF793D13 /* KVKCalendarView+Extension.swift */, 7ED99FB8A745CDF19E67E51BEDB31728 /* CurrentLineView.swift */, E0C12DDE8C694F5EAE4DCA3C5ABF45C2 /* Date+Extension.swift */, CD2FEDB8B803BCED77D12A798EF2E282 /* DayCell.swift */, @@ -347,6 +349,7 @@ F969448726F3534500EF4929 /* KVKCollectionViewCell.swift */, F9535B32272DE8CC008AAC56 /* KVKTableViewCell.swift */, F9535B34272DE9F5008AAC56 /* KVKTableViewHeaderFooterView.swift */, + F98F027E2924C38500416B9A /* SwiftUI+Extensions.swift */, ); name = KVKCalendar; path = ../..; @@ -542,15 +545,16 @@ 80F2D7EF1FD9A18404BA24798C596B10 /* CalendarData.swift in Sources */, F921AE5E2656EAFD007669C6 /* AllDayView.swift in Sources */, E7166AB631A1F363BEC98DB2BB114360 /* CalendarModel.swift in Sources */, - 71F3B422C4D8488E7E4ADD2E2A52C94D /* CalendarView+Extension.swift in Sources */, + 71F3B422C4D8488E7E4ADD2E2A52C94D /* KVKCalendarView+Extension.swift in Sources */, F9258BCA28037DED00750B5C /* ScrollableWeekHeaderTitleView.swift in Sources */, - 17BEAAC59928F37A39C7E34B5D385C0B /* CalendarView.swift in Sources */, + 17BEAAC59928F37A39C7E34B5D385C0B /* KVKCalendarView.swift in Sources */, DE29FAA9F97EB2995F905ABC8B0B5944 /* CurrentLineView.swift in Sources */, 039DBEBA9DBEC27BC9CC0E0E64D8E0F1 /* Date+Extension.swift in Sources */, 57BF1D35A352DADF2DAF529D7499F7E5 /* DayCell.swift in Sources */, 876A6638692F4F9BB514AABF621BC0C6 /* DayData.swift in Sources */, 7B2B45BE5BD852E6BC3AAA9621F088CF /* DayPadCell.swift in Sources */, BD9CD31D8BF663EAF9D3DD37FD330D42 /* DayPhoneCell.swift in Sources */, + F98F027F2924C38500416B9A /* SwiftUI+Extensions.swift in Sources */, F902B98A26A4A1810004EB05 /* DefaultTimelineEventLayout.swift in Sources */, A6389BA2A93A24D40BB15DDD11991ADE /* DayView.swift in Sources */, 09CAF424D1A4FD4B9FBCB85D1FDB1F0E /* EventView.swift in Sources */, @@ -642,7 +646,7 @@ GCC_PREFIX_HEADER = "Target Support Files/KVKCalendar/KVKCalendar-prefix.pch"; INFOPLIST_FILE = "Target Support Files/KVKCalendar/KVKCalendar-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/KVKCalendar/KVKCalendar.modulemap"; PRODUCT_MODULE_NAME = KVKCalendar; @@ -674,7 +678,7 @@ GCC_PREFIX_HEADER = "Target Support Files/KVKCalendar/KVKCalendar-prefix.pch"; INFOPLIST_FILE = "Target Support Files/KVKCalendar/KVKCalendar-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/KVKCalendar/KVKCalendar.modulemap"; PRODUCT_MODULE_NAME = KVKCalendar; @@ -740,7 +744,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -805,7 +809,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -834,7 +838,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Target Support Files/Pods-KVKCalendar_Example/Pods-KVKCalendar_Example-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; MODULEMAP_FILE = "Target Support Files/Pods-KVKCalendar_Example/Pods-KVKCalendar_Example.modulemap"; @@ -867,7 +871,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Target Support Files/Pods-KVKCalendar_Tests/Pods-KVKCalendar_Tests-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; MODULEMAP_FILE = "Target Support Files/Pods-KVKCalendar_Tests/Pods-KVKCalendar_Tests.modulemap"; @@ -900,7 +904,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Target Support Files/Pods-KVKCalendar_Tests/Pods-KVKCalendar_Tests-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; MODULEMAP_FILE = "Target Support Files/Pods-KVKCalendar_Tests/Pods-KVKCalendar_Tests.modulemap"; @@ -934,7 +938,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Target Support Files/Pods-KVKCalendar_Example/Pods-KVKCalendar_Example-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; MODULEMAP_FILE = "Target Support Files/Pods-KVKCalendar_Example/Pods-KVKCalendar_Example.modulemap"; diff --git a/KVKCalendar.podspec b/KVKCalendar.podspec index 73d491fa..01bf1261 100644 --- a/KVKCalendar.podspec +++ b/KVKCalendar.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KVKCalendar' - s.version = '0.6.11' + s.version = '0.6.12' s.summary = 'A most fully customization calendar for Apple platforms.' s.description = <<-DESC @@ -15,7 +15,7 @@ Pod::Spec.new do |s| s.author = { 'Sergei Kviatkovskii' => 'sergejkvyatkovskij@gmail.com' } s.source = { :git => 'https://github.com/kvyatkovskys/KVKCalendar.git', :tag => s.version.to_s } s.social_media_url = 'https://github.com/kvyatkovskys' - s.ios.deployment_target = '10.0' + s.ios.deployment_target = '13.0' s.source_files = 'Sources/**/*.swift' s.frameworks = 'UIKit', 'EventKit' s.swift_version = '5.0' diff --git a/README.md b/README.md index a4849f91..ef09c04d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - +   [![CI Status](https://img.shields.io/travis/kvyatkovskys/KVKCalendar.svg?style=flat)](https://travis-ci.org/kvyatkovskys/KVKCalendar) [![Version](https://img.shields.io/cocoapods/v/KVKCalendar.svg?style=flat)](https://cocoapods.org/pods/KVKCalendar) @@ -12,6 +12,7 @@ **KVKCalendar** is a most fully customization calendar. Library consists of five modules for displaying various types of calendar (*day*, *week*, *month*, *year*, *list of events*). You can choose any module or use all. It is designed based on a standard iOS calendar, but with additional features. Timeline displays the schedule for the day and the week. **Additional features:** +- [x] Switch between time zones - [x] Dark mode - [ ] Skeleton loading (month/list) - [x] Custom event view @@ -32,7 +33,7 @@ Please, use [Issues](https://github.com/kvyatkovskys/KVKCalendar/issues) only fo ## Requirements -- iOS 10.0+, iPadOS 10.0+, MacOS 11.0+ (supports Mac Catalyst) +- iOS 13.0+, iPadOS 13.0+, MacOS 11.0+ (supports Mac Catalyst) - Swift 5.0+ ## Installation @@ -182,26 +183,27 @@ func dequeueCell(parameter: CellParameter, type: CalendarType, view: T, index ## Usage for SwiftUI Add a new `SwiftUI` file and import `KVKCalendar`. -Create a struct `CalendarDisplayView` and declare the protocol `UIViewRepresentable` for connection `UIKit` with `SwiftUI`. +Create a struct `CalendarViewDisplayable` and declare the protocol `UIViewRepresentable` for connection `UIKit` with `SwiftUI`. ```swift import SwiftUI import KVKCalendar -struct CalendarDisplayView: UIViewRepresentable { - @Binding var events: [Event] +struct CalendarViewDisplayable: UIViewRepresentable { - private var calendar: CalendarView = { - return CalendarView(frame: frame, style: style) - }() + @Binding var events: [Event] + var selectDate = Date() + var style = Style() + + private var calendar = KVKalendarView(frame: .zero) - func makeUIView(context: UIViewRepresentableContext) -> CalendarView { + func makeUIView(context: UIViewRepresentableContext) -> KVKCalendarView { calendar.dataSource = context.coordinator calendar.delegate = context.coordinator return calendar } - func updateUIView(_ uiView: CalendarView, context: UIViewRepresentableContext) { + func updateUIView(_ uiView: KVKCalendarView, context: UIViewRepresentableContext) { context.coordinator.events = events } @@ -210,32 +212,34 @@ struct CalendarDisplayView: UIViewRepresentable { } public init(events: Binding<[Event]>) { - self._events = events + _events = events + calendar = KVKCalendarView(frame: frame, date: selectDate, style: style) } // MARK: Calendar DataSource and Delegate class Coordinator: NSObject, CalendarDataSource, CalendarDelegate { - private let view: CalendarDisplayView + private let view: CalendarViewDisplayable var events: [Event] = [] { didSet { + view.events = events view.calendar.reloadData() } } - init(_ view: CalendarDisplayView) { + init(_ view: CalendarViewDisplayable) { self.view = view super.init() } func eventsForCalendar(systemEvents: [EKEvent]) -> [Event] { - return events + events } } } ``` -Create a new `SwiftUI` file and add `CalendarDisplayView` to `body`. +Create a new `SwiftUI` file and add `CalendarViewDisplayable` to `body`. ```swift import SwiftUI @@ -244,8 +248,8 @@ struct CalendarContentView: View { @State var events: [Event] = [] var body: some View { - NavigationView { - CalendarDisplayView(events: $events) + NavigationStack { + CalendarViewDisplayable(events: $events) } } } diff --git a/Screenshots/ipad_month2.png b/Screenshots/ipad_month2.png new file mode 100644 index 00000000..ff0d4996 Binary files /dev/null and b/Screenshots/ipad_month2.png differ diff --git a/Screenshots/iphone_day.png b/Screenshots/iphone_day.png new file mode 100644 index 00000000..cdf88c94 Binary files /dev/null and b/Screenshots/iphone_day.png differ diff --git a/Sources/KVKCalendar/AllDayEventModel.swift b/Sources/KVKCalendar/AllDayEventModel.swift index 403785f7..e22c8953 100644 --- a/Sources/KVKCalendar/AllDayEventModel.swift +++ b/Sources/KVKCalendar/AllDayEventModel.swift @@ -9,21 +9,6 @@ import UIKit -@available(iOS 13.4, *) -extension AllDayEventView: PointerInteractionProtocol { - - func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? { - var pointerStyle: UIPointerStyle? - - if let interactionView = interaction.view { - let targetedPreview = UITargetedPreview(view: interactionView) - pointerStyle = UIPointerStyle(effect: .hover(targetedPreview)) - } - return pointerStyle - } - -} - struct AllDayEvent { let date: Date let event: Event diff --git a/Sources/KVKCalendar/AllDayEventView.swift b/Sources/KVKCalendar/AllDayEventView.swift index 671bb213..f20a8afa 100644 --- a/Sources/KVKCalendar/AllDayEventView.swift +++ b/Sources/KVKCalendar/AllDayEventView.swift @@ -45,7 +45,7 @@ final class AllDayEventView: UIView { addGestureRecognizer(tap) if #available(iOS 13.4, *) { - addPointInteraction(on: self, delegate: self) + addPointInteraction() } } diff --git a/Sources/KVKCalendar/AllDayView.swift b/Sources/KVKCalendar/AllDayView.swift index 223a99b5..eb71807c 100644 --- a/Sources/KVKCalendar/AllDayView.swift +++ b/Sources/KVKCalendar/AllDayView.swift @@ -84,9 +84,8 @@ final class AllDayView: UIView { backgroundColor = params.style.allDay.backgroundColor subviews.filter { $0.tag == 123 }.forEach { $0.removeFromSuperview() } - let widthTitle = style.timeline.allLeftOffset titleLabel.frame = CGRect(x: params.style.allDay.offsetX, y: 0, - width: widthTitle - params.style.allDay.offsetX, + width: leftOffsetWithAdditionalTime - params.style.allDay.offsetX, height: params.style.allDay.height) titleLabel.font = params.style.allDay.fontTitle titleLabel.textColor = params.style.allDay.titleColor @@ -111,10 +110,10 @@ final class AllDayView: UIView { break } - if let customHeaderCorderView = dataSource?.dequeueAllDayCornerHeader(date: params.date, + if let customHeaderCornerView = dataSource?.dequeueAllDayCornerHeader(date: params.date, frame: CGRect(origin: frame.origin, size: CGSize(width: titleLabel.frame.width, height: frame.height))) { - customHeaderCorderView.tag = 123 - addSubview(customHeaderCorderView) + customHeaderCornerView.tag = 123 + addSubview(customHeaderCornerView) } else { addSubview(titleLabel) } diff --git a/Sources/KVKCalendar/Calendar+Extension.swift b/Sources/KVKCalendar/Calendar+Extension.swift index 14973b0d..235ae388 100644 --- a/Sources/KVKCalendar/Calendar+Extension.swift +++ b/Sources/KVKCalendar/Calendar+Extension.swift @@ -122,9 +122,7 @@ extension UIApplication { } else { return false } - } else if #available(iOS 11.0, *), - let keyWindow = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) - { + } else if let keyWindow = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) { return keyWindow.safeAreaInsets.bottom > 0 } else { return false @@ -200,14 +198,6 @@ extension UICollectionView { } -public extension UIView { - - static var kvkIdentifier: String { - return String(describing: self) - } - -} - extension UITableView { func kvkRegister(_ cell: T.Type) { @@ -222,10 +212,9 @@ extension UITableView { extension UIColor { - @available(iOS 13, *) static func useForStyle(dark: UIColor, white: UIColor) -> UIColor { - return UIColor { (traitCollection: UITraitCollection) -> UIColor in - return traitCollection.userInterfaceStyle == .dark ? dark : white + UIColor { (traitCollection: UITraitCollection) -> UIColor in + traitCollection.userInterfaceStyle == .dark ? dark : white } } @@ -234,11 +223,7 @@ extension UIColor { extension UIScreen { static var isDarkMode: Bool { - if #available(iOS 12.0, *) { - return main.traitCollection.userInterfaceStyle == .dark - } else { - return false - } + main.traitCollection.userInterfaceStyle == .dark } } @@ -254,17 +239,9 @@ extension UIView { } func setRoundCorners(_ corners: UIRectCorner = .allCorners, radius: CGSize) { - if #available(iOS 11.0, *) { - setRoundCorners(corners.convertedCorners, radius: max(radius.width, radius.height)) - } else { - let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: radius) - let mask = CAShapeLayer() - mask.path = path.cgPath - layer.mask = mask - } + setRoundCorners(corners.convertedCorners, radius: max(radius.width, radius.height)) } - @available(iOS 11.0, *) func setRoundCorners(_ corners: CACornerMask = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner], radius: CGFloat) { layer.masksToBounds = true layer.cornerRadius = radius @@ -319,7 +296,47 @@ extension UIRectCorner { } -public extension UITableView { +public protocol KVKCellIdentifierProxy: AnyObject { + + static var kvkIdentifier: String { get } + +} + +extension UIView: KVKCellIdentifierProxy { + + public static var kvkIdentifier: String { + String(describing: self) + } + +} + +public protocol KVKDequeueProxyProtocol: AnyObject {} + +//public extension KVKDequeueProxyProtocol { +// +// func kvkDequeueCell(type: CalendarType, +// view: T, +// id: String = U.kvkIdentifier, +// indexPath: IndexPath? = nil, +// configure: (U) -> Void) -> U? { +// switch type { +// +// case .month: +// if let cell = (view as? UITableView)?.kvkDequeueCell(id: id, +// indexPath: indexPath, +// configure: configure) { +// return cell +// } else { +// return nil +// } +// default: +// return nil +// } +// } +// +//} + +public extension KVKDequeueProxyProtocol where Self: UITableView { func kvkDequeueCell(id: String = T.kvkIdentifier, indexPath: IndexPath? = nil, configure: (T) -> Void) -> T { kvkRegister(T.self) @@ -353,7 +370,7 @@ public extension UITableView { } -public extension UICollectionView { +public extension KVKDequeueProxyProtocol where Self: UICollectionView { func kvkDequeueCell(id: String = T.kvkIdentifier, indexPath: IndexPath, configure: (T) -> Void) -> T { kvkRegister(T.self, id: id) @@ -385,19 +402,25 @@ public extension UICollectionView { } -@available(iOS 13.4, *) -protocol PointerInteractionProtocol: UIPointerInteractionDelegate { - - func addPointInteraction(on view: UIView, delegate: UIPointerInteractionDelegate) - -} +extension UITableView: KVKDequeueProxyProtocol {} +extension UICollectionView: KVKDequeueProxyProtocol {} @available(iOS 13.4, *) -extension PointerInteractionProtocol { +extension UIView: UIPointerInteractionDelegate { - func addPointInteraction(on view: UIView, delegate: UIPointerInteractionDelegate) { - let interaction = UIPointerInteraction(delegate: delegate) - view.addInteraction(interaction) + func addPointInteraction() { + let interaction = UIPointerInteraction(delegate: self) + addInteraction(interaction) + } + + public func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? { + var pointerStyle: UIPointerStyle? + + if let interactionView = interaction.view { + let targetedPreview = UITargetedPreview(view: interactionView) + pointerStyle = UIPointerStyle(effect: .highlight(targetedPreview)) + } + return pointerStyle } } diff --git a/Sources/KVKCalendar/CalendarModel.swift b/Sources/KVKCalendar/CalendarModel.swift index 08dc0cb9..e3b4ddcd 100644 --- a/Sources/KVKCalendar/CalendarModel.swift +++ b/Sources/KVKCalendar/CalendarModel.swift @@ -78,10 +78,24 @@ public enum TimeHourSystem: Int { return "HH:mm" } } + + public var shortFormat: String { + switch self { + case .twelveHour, .twelve: + return "h a" + case .twentyFourHour, .twentyFour: + return "HH:mm" + } + } } -public enum CalendarType: String, CaseIterable { +public enum CalendarType: String, CaseIterable, ItemsMenuProxy { case day, week, month, year, list + + public var title: String { + rawValue.capitalized + } + } extension CalendarType: Identifiable { @@ -124,7 +138,7 @@ public struct Event { public var ID: String @available(swift, deprecated: 0.5.8, obsoleted: 0.5.9, renamed: "title") - public var text: String = "" + public var text: String? public var title: TextEvent = TextEvent() public var start: Date = Date() @@ -155,7 +169,7 @@ public struct Event { public var recurringType: Event.RecurringType = .none ///custom style - ///(in-progress) works only with a default height + ///(in-progress) works only with a default (widht & height) public var style: EventStyle? = nil public var systemEvent: EKEvent? = nil @@ -247,8 +261,8 @@ public extension Event { } struct Color { - let value: UIColor - let alpha: CGFloat + public let value: UIColor + public let alpha: CGFloat public init(_ color: UIColor, alpha: CGFloat = 0.3) { self.value = color @@ -340,6 +354,64 @@ extension CalendarSettingProtocol { func setDate(_ date: Date, animated: Bool) {} func setUI(reload: Bool = false) {} + var actualSelectedTimeZoneCount: CGFloat { + guard style.selectedTimeZones.count > 1 else { return 0 } + + return CGFloat(style.selectedTimeZones.count) + } + + var leftOffsetWithAdditionalTime: CGFloat { + guard actualSelectedTimeZoneCount > 0 else { + return style.timeline.allLeftOffset + } + + return (actualSelectedTimeZoneCount * style.timeline.widthTime) + style.timeline.offsetTimeX + style.timeline.offsetLineLeft + } + + func changeToTimeZone(_ hour: Int, from: TimeZone, to: TimeZone) -> Date { + let today = Date() + let components = DateComponents(year: today.kvkYear, + month: today.kvkMonth, + day: today.kvkDay, + hour: hour, + minute: 0) + let date = Calendar.current.date(from: components) ?? today + let sourceOffset = from.secondsFromGMT(for: date) + let destinationOffset = to.secondsFromGMT(for: date) + let timeInterval = TimeInterval(destinationOffset - sourceOffset) + return Date(timeInterval: timeInterval, since: date) + } + + func handleTimelineLabel(zones: [TimeZone], + label: TimelineLabel) -> (current: TimelineLabel, others: [UILabel])? { + var otherLabels = [UILabel]() + let current = label + + zones.enumerated().forEach { + let x = (CGFloat($0.offset) * current.frame.width) + style.timeline.offsetTimeX + let otherLabel = UILabel(frame: CGRect(x: x, y: current.frame.origin.y, + width: current.frame.width, height: current.frame.height)) + let labelDate = changeToTimeZone(label.hashTime, from: style.timezone, to: $0.element) + otherLabel.text = timeFormatter(date: labelDate, format: style.timeSystem.shortFormat) + otherLabel.textAlignment = style.timeline.timeAlignment + otherLabel.font = style.timeline.timeFont + otherLabel.adjustsFontSizeToFitWidth = true + + if $0.element.identifier == style.timezone.identifier { + current.frame = otherLabel.frame + } else { + otherLabels.append(otherLabel) + } + } + + return (current, otherLabels) + } + + func timeFormatter(date: Date, format: String) -> String { + let formatter = DateFormatter() + formatter.dateFormat = format + return formatter.string(from: date) + } } // MARK: - Data source protocol @@ -397,7 +469,7 @@ public protocol CalendarDataSource: AnyObject { func dequeueTimeLabel(_ label: TimelineLabel) -> (current: TimelineLabel, others: [UILabel])? - func dequeueCornerHeader(date: Date, frame: CGRect) -> UIView? + func dequeueCornerHeader(date: Date, frame: CGRect, type: CalendarType) -> UIView? func dequeueAllDayCornerHeader(date: Date, frame: CGRect) -> UIView? } @@ -435,7 +507,7 @@ public extension CalendarDataSource { func dequeueTimeLabel(_ label: TimelineLabel) -> (current: TimelineLabel, others: [UILabel])? { nil } - func dequeueCornerHeader(date: Date, frame: CGRect) -> UIView? { nil } + func dequeueCornerHeader(date: Date, frame: CGRect, type: CalendarType) -> UIView? { nil } func dequeueAllDayCornerHeader(date: Date, frame: CGRect) -> UIView? { nil } @@ -493,6 +565,8 @@ public protocol CalendarDelegate: AnyObject { /// deselect event on timeline func didDeselectEvent(_ event: Event, animated: Bool) + + func didUpdateStyle(_ style: Style, type: CalendarType) } public extension CalendarDelegate { @@ -523,6 +597,8 @@ public extension CalendarDelegate { func didDeselectEvent(_ event: Event, animated: Bool) {} func didChangeViewerFrame(_ frame: CGRect) {} + + func didUpdateStyle(_ style: Style, type: CalendarType) {} } // MARK: - Private Display dataSource diff --git a/Sources/KVKCalendar/CurrentLineView.swift b/Sources/KVKCalendar/CurrentLineView.swift index e01225c0..963a1a67 100644 --- a/Sources/KVKCalendar/CurrentLineView.swift +++ b/Sources/KVKCalendar/CurrentLineView.swift @@ -21,7 +21,7 @@ final class CurrentLineView: UIView { let label = TimelineLabel() label.textAlignment = .center label.adjustsFontSizeToFitWidth = true - label.minimumScaleFactor = 0.6 + label.minimumScaleFactor = 0.4 label.hashTime = Date().kvkMinute return label }() @@ -78,23 +78,14 @@ extension CurrentLineView: CalendarSettingProtocol { dotView.backgroundColor = style.timeline.currentLineHourColor formatter.dateFormat = style.timeSystem.format - formatter.timeZone = style.timezone - formatter.locale = style.locale timeLabel.textColor = style.timeline.currentLineHourColor timeLabel.font = style.timeline.currentLineHourFont - let widthOffset: CGFloat -#if targetEnvironment(macCatalyst) - widthOffset = 20 -#else - widthOffset = 5 -#endif - timeLabel.frame = CGRect(x: 0, y: 0, - width: style.timeline.currentLineHourWidth - widthOffset, + width: style.timeline.currentLineHourWidth, height: frame.height) - dotView.frame = CGRect(x: style.timeline.allLeftOffset - (style.timeline.currentLineHourDotSize.width * 0.5) - frame.origin.x, + dotView.frame = CGRect(x: leftOffsetWithAdditionalTime - (style.timeline.currentLineHourDotSize.width * 0.5), y: (frame.height * 0.5) - 2, width: style.timeline.currentLineHourDotSize.width, height: style.timeline.currentLineHourDotSize.height) @@ -117,6 +108,10 @@ extension CurrentLineView: CalendarSettingProtocol { self.frame.size.width = frame.width self.frame.origin.x = frame.origin.x } + + func setOffsetForTime(_ offset: CGFloat) { + timeLabel.frame.origin.x = offset + } } #endif diff --git a/Sources/KVKCalendar/Date+Extension.swift b/Sources/KVKCalendar/Date+Extension.swift index 2168ea7c..04ffea74 100644 --- a/Sources/KVKCalendar/Date+Extension.swift +++ b/Sources/KVKCalendar/Date+Extension.swift @@ -8,6 +8,14 @@ import Foundation public extension Date { + + private init(year: Int, month: Int, day: Int, hour: Int, minute: Int) { + self.init() + let isoDate = "\(year)-\(month)-\(day)T\(hour):\(minute):00+0000" + let dateFormatter = ISO8601DateFormatter() + self = dateFormatter.date(from: isoDate) ?? self + } + func titleForLocale(_ locale: Locale, formatter: DateFormatter) -> String { formatter.locale = locale return formatter.string(from: self) diff --git a/Sources/KVKCalendar/DayCell.swift b/Sources/KVKCalendar/DayCell.swift index 27a799ef..e13c3743 100644 --- a/Sources/KVKCalendar/DayCell.swift +++ b/Sources/KVKCalendar/DayCell.swift @@ -118,7 +118,7 @@ class DayCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) if #available(iOS 13.4, *) { - addPointInteraction(on: self, delegate: self) + addPointInteraction() } } @@ -152,19 +152,4 @@ class DayCell: UICollectionViewCell { } } -@available(iOS 13.4, *) -extension DayCell: PointerInteractionProtocol { - - func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? { - var pointerStyle: UIPointerStyle? - - if let interactionView = interaction.view { - let targetedPreview = UITargetedPreview(view: interactionView) - pointerStyle = UIPointerStyle(effect: .highlight(targetedPreview)) - } - return pointerStyle - } - -} - #endif diff --git a/Sources/KVKCalendar/DayView.swift b/Sources/KVKCalendar/DayView.swift index d16cf063..756d6420 100644 --- a/Sources/KVKCalendar/DayView.swift +++ b/Sources/KVKCalendar/DayView.swift @@ -77,13 +77,7 @@ final class DayView: UIView { @discardableResult private func updateEventViewer(frame: CGRect) -> CGRect? { var viewerFrame = frame - // hard reset the width when we change the orientation - if UIApplication.shared.orientation.isPortrait { - viewerFrame.size.width = bounds.width * 0.5 - viewerFrame.origin.x = viewerFrame.width - } else { - viewerFrame.origin.x = bounds.width - viewerFrame.width - } + viewerFrame.origin.x = bounds.width - viewerFrame.width guard let eventViewer = dataSource?.willDisplayEventViewer(date: parameters.data.date, frame: viewerFrame) else { return nil } @@ -142,7 +136,7 @@ extension DayView: TimelineDelegate { delegate?.didAddNewEvent(event, date) } - func didChangeEvent(_ event: Event, minute: Int, hour: Int, point: CGPoint, newDay: Int?) { + func didChangeEvent(_ event: Event, minute: Int, hour: Int, point: CGPoint, newDate: Date?) { var startComponents = DateComponents() startComponents.year = event.start.kvkYear startComponents.month = event.start.kvkMonth @@ -164,6 +158,9 @@ extension DayView: TimelineDelegate { delegate?.didChangeEvent(event, start: startDate, end: endDate) } + func dequeueTimeLabel(_ label: TimelineLabel) -> (current: TimelineLabel, others: [UILabel])? { + handleTimelineLabel(zones: style.selectedTimeZones, label: label) + } } extension DayView: CalendarSettingProtocol { @@ -197,16 +194,7 @@ extension DayView: CalendarSettingProtocol { if let idx = subviews.firstIndex(where: { $0.tag == tagEventViewer }) { subviews[idx].removeFromSuperview() var viewerFrame = timelineFrame - - let width: CGFloat - if UIDevice.current.orientation.isPortrait { - width = frame.width * 0.5 - timelineFrame.size.width = frame.width - width - } else { - width = defaultWidth - } - - viewerFrame.size.width = width + viewerFrame.size.width = defaultWidth if let resultViewerFrame = updateEventViewer(frame: viewerFrame) { // notify when we did change the frame of viewer delegate?.didChangeViewerFrame(resultViewerFrame) @@ -253,6 +241,7 @@ extension DayView: CalendarSettingProtocol { guard let self = self else { return } let newTimeline = self.createTimelineView(frame: self.timelinePage.bounds) + newTimeline.updateStyle(self.style, force: reload) switch type { case .next: self.nextDate() @@ -376,6 +365,11 @@ extension DayView: CalendarSettingProtocol { self.timelinePage.addNewTimelineView(newTimeline, to: .begin) } } + view.didUpdateStyle = { [weak self] (type) in + guard let self = self else { return } + + self.delegate?.didUpdateStyle(self.scrollableWeekView.style, type: type) + } return view } diff --git a/Sources/KVKCalendar/DefaultTimelineEventLayout.swift b/Sources/KVKCalendar/DefaultTimelineEventLayout.swift index 9d70f3c2..c385b7e5 100644 --- a/Sources/KVKCalendar/DefaultTimelineEventLayout.swift +++ b/Sources/KVKCalendar/DefaultTimelineEventLayout.swift @@ -24,6 +24,7 @@ public struct DefaultTimelineEventLayout: TimelineEventLayout { switch viewMode { case .default: + let pageWidth = context.pageFrame.width + context.pageFrame.origin.x let crossEvents = context.calculateCrossEvents(forEvents: events) events.forEach { (event) in var frame = context.getEventRect(start: event.start, @@ -38,8 +39,8 @@ public struct DefaultTimelineEventLayout: TimelineEventLayout { var newWidth = frame.width newWidth /= CGFloat(crossEvent.events.count) newWidth -= context.style.timeline.offsetEvent - frame.size.width = newWidth - + frame.size.width = event.style?.defaultWidth ?? newWidth + if crossEvent.events.count > 1 { rects.forEach { (rect) in while rect.intersects(CGRect(x: newX, @@ -50,10 +51,26 @@ public struct DefaultTimelineEventLayout: TimelineEventLayout { } } } + + // when the current event exceeds a certain frame + if newX >= pageWidth { + let value = frame.width * 0.5 + let lastIdx = rects.count - 1 + if var lastUpdatedRect = rects[safe: lastIdx] { + lastUpdatedRect.size.width -= value + rects.removeLast() + rects.append(lastUpdatedRect) + } + newX -= value + } + // sometimes the event width is large than the page width + let newEventWidth = newX + frame.width + if newEventWidth > pageWidth { + frame.size.width -= newEventWidth - pageWidth + context.style.timeline.offsetEvent + } frame.origin.x = newX } - rects.append(frame) } case .list: diff --git a/Sources/KVKCalendar/DividerView.swift b/Sources/KVKCalendar/DividerView.swift index 33bd1867..458f8e0f 100644 --- a/Sources/KVKCalendar/DividerView.swift +++ b/Sources/KVKCalendar/DividerView.swift @@ -72,12 +72,12 @@ extension DividerView: CalendarSettingProtocol { } func setUI() { - timeLabel.frame = CGRect(x: style.timeline.offsetTimeX + style.timeline.cornerHeaderWidth, + timeLabel.frame = CGRect(x: style.timeline.offsetTimeX, y: 0, width: style.timeline.widthTime, height: style.timeline.heightTime) - let xLine = timeLabel.bounds.width + style.timeline.offsetTimeX + style.timeline.offsetLineLeft + style.timeline.cornerHeaderWidth + let xLine = timeLabel.bounds.width + style.timeline.offsetTimeX + style.timeline.offsetLineLeft lineView.frame = CGRect(x: xLine, y: timeLabel.center.y, width: bounds.width - xLine, diff --git a/Sources/KVKCalendar/EventView.swift b/Sources/KVKCalendar/EventView.swift index 7202ed99..6c6e0438 100644 --- a/Sources/KVKCalendar/EventView.swift +++ b/Sources/KVKCalendar/EventView.swift @@ -60,7 +60,7 @@ final class EventView: EventViewGeneral { addSubview(textView) if #available(iOS 13.4, *) { - addPointInteraction(on: self, delegate: self) + addPointInteraction() } } @@ -88,7 +88,7 @@ final class EventView: EventViewGeneral { } button.menu = menu - addPointInteraction(on: button, delegate: self) + addPointInteraction() addSubview(button) } @@ -126,19 +126,4 @@ final class EventView: EventViewGeneral { } -@available(iOS 13.4, *) -extension EventView: PointerInteractionProtocol { - - func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? { - var pointerStyle: UIPointerStyle? - - if let interactionView = interaction.view { - let targetedPreview = UITargetedPreview(view: interactionView) - pointerStyle = UIPointerStyle(effect: .hover(targetedPreview)) - } - return pointerStyle - } - -} - #endif diff --git a/Sources/KVKCalendar/EventViewGeneral.swift b/Sources/KVKCalendar/EventViewGeneral.swift index b761f12d..614f05fc 100644 --- a/Sources/KVKCalendar/EventViewGeneral.swift +++ b/Sources/KVKCalendar/EventViewGeneral.swift @@ -164,7 +164,7 @@ open class EventViewGeneral: UIView, CalendarTimer { } } - public override func draw(_ rect: CGRect) { + open override func draw(_ rect: CGRect) { guard let context = UIGraphicsGetCurrentContext() else { return } context.interpolationQuality = .none diff --git a/Sources/KVKCalendar/CalendarView+Extension.swift b/Sources/KVKCalendar/KVKCalendarView+Extension.swift similarity index 96% rename from Sources/KVKCalendar/CalendarView+Extension.swift rename to Sources/KVKCalendar/KVKCalendarView+Extension.swift index ef1306ab..3469bd69 100644 --- a/Sources/KVKCalendar/CalendarView+Extension.swift +++ b/Sources/KVKCalendar/KVKCalendarView+Extension.swift @@ -1,5 +1,5 @@ // -// CalendarView+Extension.swift +// KVKCalendarView+Extension.swift // KVKCalendar // // Created by Sergei Kviatkovskii on 14.12.2020. @@ -10,7 +10,7 @@ import UIKit import EventKit -extension CalendarView { +extension KVKCalendarView { // MARK: Public methods /// **DEPRECATED** @@ -225,7 +225,7 @@ extension CalendarView { } } -extension CalendarView: DisplayDataSource { +extension KVKCalendarView: DisplayDataSource { public func dequeueCell(parameter: CellParameter, type: CalendarType, view: T, indexPath: IndexPath) -> KVKCalendarCellProtocol? where T : UIScrollView { dataSource?.dequeueCell(parameter: parameter, type: type, view: view, indexPath: indexPath) } @@ -276,13 +276,13 @@ extension CalendarView: DisplayDataSource { dataSource?.dequeueAllDayCornerHeader(date: date, frame: frame) } - public func dequeueCornerHeader(date: Date, frame: CGRect) -> UIView? { - dataSource?.dequeueCornerHeader(date: date, frame: frame) + public func dequeueCornerHeader(date: Date, frame: CGRect, type: CalendarType) -> UIView? { + dataSource?.dequeueCornerHeader(date: date, frame: frame, type: type) } } -extension CalendarView: DisplayDelegate { +extension KVKCalendarView: DisplayDelegate { public func sizeForHeader(_ date: Date?, type: CalendarType) -> CGSize? { delegate?.sizeForHeader(date, type: type) } @@ -330,9 +330,15 @@ extension CalendarView: DisplayDelegate { public func willSelectDate(_ date: Date, type: CalendarType) { delegate?.willSelectDate(date, type: type) } + + public func didUpdateStyle(_ style: Style, type: CalendarType) { + updateStyle(style) + reloadData() + delegate?.didUpdateStyle(style, type: type) + } } -extension CalendarView { +extension KVKCalendarView { public var style: Style { get { diff --git a/Sources/KVKCalendar/CalendarView.swift b/Sources/KVKCalendar/KVKCalendarView.swift similarity index 95% rename from Sources/KVKCalendar/CalendarView.swift rename to Sources/KVKCalendar/KVKCalendarView.swift index 06e761df..a076cff1 100644 --- a/Sources/KVKCalendar/CalendarView.swift +++ b/Sources/KVKCalendar/KVKCalendarView.swift @@ -10,7 +10,10 @@ import UIKit import EventKit -public final class CalendarView: UIView { +@available(swift, deprecated: 0.6.11, obsoleted: 0.6.12, renamed: "KVKCalendarView") +public final class CalendarView: UIView {} + +public final class KVKCalendarView: UIView { struct Parameters { var type = CalendarType.day diff --git a/Sources/KVKCalendar/ListViewCell.swift b/Sources/KVKCalendar/ListViewCell.swift index 4454ed1e..b610010f 100644 --- a/Sources/KVKCalendar/ListViewCell.swift +++ b/Sources/KVKCalendar/ListViewCell.swift @@ -45,12 +45,7 @@ final class ListViewCell: KVKTableViewCell { dotView.translatesAutoresizingMaskIntoConstraints = false txtLabel.translatesAutoresizingMaskIntoConstraints = false - let leftDot: NSLayoutConstraint - if #available(iOS 11.0, *) { - leftDot = dotView.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor) - } else { - leftDot = dotView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 15) - } + let leftDot = dotView.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor) let centerYDot = dotView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) let widthDot = dotView.widthAnchor.constraint(equalToConstant: 20) let heightDot = dotView.heightAnchor.constraint(equalToConstant: 20) @@ -61,9 +56,8 @@ final class ListViewCell: KVKTableViewCell { let leftTxt = txtLabel.leftAnchor.constraint(equalTo: dotView.rightAnchor, constant: 10) let rightTxt = txtLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -15) NSLayoutConstraint.activate([topTxt, bottomTxt, leftTxt, rightTxt]) - if #available(iOS 13.4, *) { - addPointInteraction(on: self, delegate: self) + addPointInteraction() } } @@ -73,8 +67,7 @@ final class ListViewCell: KVKTableViewCell { override func setSkeletons(_ skeletons: Bool, insets: UIEdgeInsets = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4), - cornerRadius: CGFloat = 2) - { + cornerRadius: CGFloat = 2) { isUserInteractionEnabled = !skeletons txtLabel.isHidden = skeletons dotView.isHidden = skeletons @@ -93,19 +86,4 @@ final class ListViewCell: KVKTableViewCell { } -@available(iOS 13.4, *) -extension ListViewCell: PointerInteractionProtocol { - - func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? { - var pointerStyle: UIPointerStyle? - - if let interactionView = interaction.view { - let targetedPreview = UITargetedPreview(view: interactionView) - pointerStyle = UIPointerStyle(effect: .highlight(targetedPreview)) - } - return pointerStyle - } - -} - #endif diff --git a/Sources/KVKCalendar/MonthCell.swift b/Sources/KVKCalendar/MonthCell.swift index 798f6a51..acc97682 100644 --- a/Sources/KVKCalendar/MonthCell.swift +++ b/Sources/KVKCalendar/MonthCell.swift @@ -269,7 +269,7 @@ final class MonthCell: KVKCollectionViewCell { contentView.addSubview(dateLabel) if #available(iOS 13.4, *) { - addPointInteraction(on: self, delegate: self) + contentView.addPointInteraction() } } @@ -476,21 +476,6 @@ extension MonthCell: UIGestureRecognizerDelegate { } -@available(iOS 13.4, *) -extension MonthCell: PointerInteractionProtocol { - - func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? { - var pointerStyle: UIPointerStyle? - - if let interactionView = interaction.view { - let targetedPreview = UITargetedPreview(view: interactionView) - pointerStyle = UIPointerStyle(effect: .hover(targetedPreview)) - } - return pointerStyle - } - -} - protocol MonthCellDelegate: AnyObject { func didSelectEvent(_ event: Event, frame: CGRect?) diff --git a/Sources/KVKCalendar/ResizeEventView.swift b/Sources/KVKCalendar/ResizeEventView.swift index 9f534641..83975d83 100644 --- a/Sources/KVKCalendar/ResizeEventView.swift +++ b/Sources/KVKCalendar/ResizeEventView.swift @@ -112,7 +112,7 @@ final class ResizeEventView: UIView { bottomView.addSubview(bottomCircleView) if #available(iOS 13.4, *) { - addPointInteraction(on: self, delegate: self) + addPointInteraction() } } @@ -144,17 +144,7 @@ final class ResizeEventView: UIView { } @available(iOS 13.4, *) -extension ResizeEventView: PointerInteractionProtocol { - - func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? { - var pointerStyle: UIPointerStyle? - - if let interactionView = interaction.view { - let targetedPreview = UITargetedPreview(view: interactionView) - pointerStyle = UIPointerStyle(effect: .highlight(targetedPreview)) - } - return pointerStyle - } +extension ResizeEventView { func pointerInteraction(_ interaction: UIPointerInteraction, regionFor request: UIPointerRegionRequest, defaultRegion: UIPointerRegion) -> UIPointerRegion? { if topView.frame.contains(request.location) { diff --git a/Sources/KVKCalendar/ScrollableWeekHeaderTitleView.swift b/Sources/KVKCalendar/ScrollableWeekHeaderTitleView.swift index b18e51da..6059f8c7 100644 --- a/Sources/KVKCalendar/ScrollableWeekHeaderTitleView.swift +++ b/Sources/KVKCalendar/ScrollableWeekHeaderTitleView.swift @@ -32,7 +32,11 @@ final class ScrollableWeekHeaderTitleView: UIView { } } - private let titleLabel = UILabel() + private let titleLabel: UILabel = { + let lbl = UILabel() + lbl.adjustsFontSizeToFitWidth = true + return lbl + }() override init(frame: CGRect) { super.init(frame: frame) diff --git a/Sources/KVKCalendar/ScrollableWeekView.swift b/Sources/KVKCalendar/ScrollableWeekView.swift index db57d64f..7e045664 100644 --- a/Sources/KVKCalendar/ScrollableWeekView.swift +++ b/Sources/KVKCalendar/ScrollableWeekView.swift @@ -14,6 +14,7 @@ final class ScrollableWeekView: UIView { var didTrackScrollOffset: ((CGFloat?, Bool) -> Void)? var didSelectDate: ((Date?, CalendarType) -> Void)? var didChangeDay: ((TimelinePageView.SwitchPageType) -> Void)? + var didUpdateStyle: ((CalendarType) -> Void)? struct Parameters { let frame: CGRect @@ -82,6 +83,13 @@ final class ScrollableWeekView: UIView { private var titleView: ScrollableWeekHeaderTitleView? private let bottomLineView = UIView() + private let cornerBtn: UIButton = { + let btn = UIButton(type: .system) + btn.setTitleColor(.systemRed, for: .normal) + btn.setTitleColor(.lightGray, for: .selected) + btn.titleLabel?.font = .systemFont(ofSize: 17) + return btn + }() init(parameters: Parameters) { self.params = parameters @@ -146,14 +154,7 @@ final class ScrollableWeekView: UIView { } private func createCollectionView(frame: CGRect, isScrollEnabled: Bool) -> UICollectionView { - let x: CGFloat - if type == .day { - x = 0 - } else { - x = style.timeline.allLeftOffset - } - let newFrame = CGRect(x: x, y: frame.origin.y, width: frame.width - x, height: frame.height) - let collection = UICollectionView(frame: newFrame, collectionViewLayout: layout) + let collection = UICollectionView(frame: frame, collectionViewLayout: layout) collection.isPagingEnabled = true collection.showsHorizontalScrollIndicator = false collection.backgroundColor = .clear @@ -239,20 +240,69 @@ extension ScrollableWeekView: CalendarSettingProtocol { collectionView.reloadData() addSubview(customView) } else { - calculateFrameForCollectionViewIfNeeded(&mainFrame) - collectionView = createCollectionView(frame: mainFrame, - isScrollEnabled: style.headerScroll.isScrollEnabled) - addSubview(collectionView) - addTitleHeaderIfNeeded(frame: collectionView.frame) - if let cornerHeader = dataSource?.dequeueCornerHeader(date: date, frame: CGRect(x: 0, y: 0, - width: collectionView.frame.origin.x, - height: bounds.height)) { + width: leftOffsetWithAdditionalTime, + height: bounds.height), + type: type) { addSubview(cornerHeader) - } else if Platform.currentInterface != .phone { + mainFrame.origin.x = cornerHeader.frame.width + mainFrame.size.width -= cornerHeader.frame.width + } else if style.timeline.useDefaultCorderHeader { + cornerBtn.frame = CGRect(x: 0, y: 0, + width: leftOffsetWithAdditionalTime, + height: bounds.height) + cornerBtn.setTitle(style.timezone.abbreviation(), for: .normal) + cornerBtn.titleLabel?.adjustsFontSizeToFitWidth = true + addSubview(cornerBtn) + + if #available(iOS 14.0, *) { + cornerBtn.showsMenuAsPrimaryAction = true + cornerBtn.addPointInteraction() + cornerBtn.menu = createTimeZonesMenu() + + if style.selectedTimeZones.count > 1 { + cornerBtn.frame.size.height -= 35 + + let actions: [UIAction] = style.selectedTimeZones.compactMap { (item) in + UIAction(title: item.abbreviation() ?? "-") { [weak self] (_) in + self?.style.timezone = item + self?.didUpdateStyle?(self?.type ?? .day) + } + } + let sgObject = UISegmentedControl(frame: CGRect(x: 2, + y: cornerBtn.frame.height + 5, + width: cornerBtn.frame.width - 4, + height: 25), + actions: actions) + sgObject.selectedSegmentIndex = style.selectedTimeZones.firstIndex(where: { $0.identifier == style.timezone.identifier }) ?? 0 + let sizeFont: CGFloat + if Platform.currentInterface == .phone { + sizeFont = 8 + } else { + sizeFont = 10 + } + let defaultAttributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: sizeFont)] + sgObject.setTitleTextAttributes(defaultAttributes, for: .normal) + addSubview(sgObject) + } + } else { + // Fallback on earlier versions + } + + mainFrame.origin.x = cornerBtn.frame.width + mainFrame.size.width -= cornerBtn.frame.width + } + + if Platform.currentInterface != .phone { titleView?.frame.origin.x = 10 } + + calculateFrameForCollectionViewIfNeeded(&mainFrame) + collectionView = createCollectionView(frame: mainFrame, + isScrollEnabled: style.headerScroll.isScrollEnabled) + addSubview(collectionView) + addTitleHeaderIfNeeded(frame: collectionView.frame) } addSubview(bottomLineView) @@ -406,4 +456,33 @@ extension ScrollableWeekView: UICollectionViewDelegate, UICollectionViewDelegate } } +extension ScrollableWeekView { + + private func createTimeZonesMenu() -> UIMenu { + let actions = style.timeZoneIds.compactMap { (item) in + let alreadySelected = style.selectedTimeZones.contains(where: { $0.identifier == item }) + + return UIAction(title: item, state: alreadySelected ? .on : .off) { [weak self] (_) in + guard let timeZone = TimeZone(identifier: item) else { return } + + if alreadySelected { + self?.style.selectedTimeZones.removeAll(where: { $0.identifier == item }) + if self?.style.timezone.identifier == item { + self?.style.timezone = self?.style.selectedTimeZones.last ?? TimeZone.current + } + } else { + self?.style.timezone = timeZone + self?.style.selectedTimeZones.append(timeZone) + if (self?.style.selectedTimeZones.count ?? 0) > 3 { + self?.style.selectedTimeZones.removeFirst() + } + } + self?.didUpdateStyle?(self?.type ?? .day) + } + } + return UIMenu(title: "List of Time zones", children: actions) + } + +} + #endif diff --git a/Sources/KVKCalendar/Style.swift b/Sources/KVKCalendar/Style.swift index 0a5ba61a..f095ff09 100644 --- a/Sources/KVKCalendar/Style.swift +++ b/Sources/KVKCalendar/Style.swift @@ -28,12 +28,16 @@ public struct Style { public var startWeekDay: StartDayType = .monday public var followInSystemTheme: Bool = true public var systemCalendars: Set = [] + /// Linux timezone identifiers + public var timeZoneIds: [String] = ["America/New_York", "Europe/London", "Europe/Moscow", "Asia/Tokyo"] + public var selectedTimeZones: [TimeZone] public init(configureAsDefaultCalendar: Bool = true) { + selectedTimeZones = [timezone] + guard configureAsDefaultCalendar else { return } if Platform.currentInterface == .phone { - timeline.currentLineHourWidth = 45 timeline.offsetTimeX = 2 timeline.offsetLineLeft = 2 headerScroll.titleDateAlignment = .center @@ -44,7 +48,7 @@ public struct Style { month.weekDayAlignment = .center month.isHiddenSeparatorOnEmptyDate = true month.colorBackgroundWeekendDate = .clear - month.fontTitleHeader = .boldSystemFont(ofSize: 19) + month.fontTitleHeader = .boldSystemFont(ofSize: 19) month.isHiddenSectionHeader = false month.isHiddenTitleHeader = true week.colorBackground = .white @@ -113,7 +117,7 @@ public struct HeaderScrollStyle { @available(swift, deprecated: 0.5.9, obsoleted: 0.6.0, renamed: "titleDateColor") public var colorTitleDate: UIColor = .black public var titleDateColor: UIColor = .black - + @available(swift, deprecated: 0.5.9, obsoleted: 0.6.0, renamed: "titleDateColorCorner") public var colorTitleCornerDate: UIColor = .red public var titleDateColorCorner: UIColor = .red @@ -180,7 +184,6 @@ public struct TimelineStyle { public var widthTime: CGFloat = 40 public var heightTime: CGFloat = 20 public var offsetTimeX: CGFloat = 10 - public var cornerHeaderWidth: CGFloat = 0 public var offsetTimeY: CGFloat = 25 public var timeColor: UIColor = .systemGray public var timeAlignment: NSTextAlignment = .center @@ -199,7 +202,7 @@ public struct TimelineStyle { public var currentLineHourColor: UIColor = .red public var currentLineHourDotSize: CGSize = CGSize(width: 5, height: 5) public var currentLineHourDotCornersRadius: CGSize = CGSize(width: 2.5, height: 2.5) - public var currentLineHourWidth: CGFloat = 60 + public var currentLineHourWidth: CGFloat = 40 public var currentLineHourHeight: CGFloat = 1 public var separatorLineColor: UIColor = .gray public var movingMinutesColor: UIColor = .systemBlue @@ -219,9 +222,10 @@ public struct TimelineStyle { public var timeDividerColor: UIColor = .lightGray public var timeDividerFont: UIFont = .systemFont(ofSize: 10) public var scale: Scale? = Scale(min: 1, max: 6) + public var useDefaultCorderHeader = false public var allLeftOffset: CGFloat { - widthTime + offsetTimeX + offsetLineLeft + cornerHeaderWidth + widthTime + offsetTimeX + offsetLineLeft } public enum ViewMode: Int { @@ -572,103 +576,101 @@ extension Style { guard followInSystemTheme else { return self } var newStyle = self - if #available(iOS 13.0, *) { - // event - newStyle.event.colorIconFile = UIColor.useForStyle(dark: .systemGray, white: newStyle.event.colorIconFile) - - // header - newStyle.headerScroll.colorNameEmptyDay = UIColor.useForStyle(dark: .systemGray6, - white: newStyle.headerScroll.colorNameEmptyDay) - newStyle.headerScroll.colorBackground = UIColor.useForStyle(dark: .black, white: newStyle.headerScroll.colorBackground) - newStyle.headerScroll.titleDateColor = UIColor.useForStyle(dark: .white, white: newStyle.headerScroll.titleDateColor) - newStyle.headerScroll.titleDateColorCorner = UIColor.useForStyle(dark: .systemRed, - white: newStyle.headerScroll.titleDateColorCorner) - newStyle.headerScroll.colorDate = UIColor.useForStyle(dark: .white, white: newStyle.headerScroll.colorDate) - newStyle.headerScroll.colorNameDay = UIColor.useForStyle(dark: .white, white: newStyle.headerScroll.colorNameDay) - newStyle.headerScroll.colorCurrentDate = UIColor.useForStyle(dark: .systemGray6, - white: newStyle.headerScroll.colorCurrentDate) - newStyle.headerScroll.colorBackgroundCurrentDate = UIColor.useForStyle(dark: .systemRed, - white: newStyle.headerScroll.colorBackgroundCurrentDate) - newStyle.headerScroll.colorBackgroundSelectDate = UIColor.useForStyle(dark: .white, - white: newStyle.headerScroll.colorBackgroundSelectDate) - newStyle.headerScroll.colorSelectDate = UIColor.useForStyle(dark: .black, white: newStyle.headerScroll.colorSelectDate) - newStyle.headerScroll.colorCurrentSelectDateForDarkStyle = UIColor.useForStyle(dark: .white, - white: newStyle.headerScroll.colorCurrentSelectDateForDarkStyle) - newStyle.headerScroll.colorWeekendDate = UIColor.useForStyle(dark: .systemGray2, - white: newStyle.headerScroll.colorWeekendDate) - newStyle.headerScroll.bottomLineColor = UIColor.useForStyle(dark: .systemGray2, - white: newStyle.headerScroll.bottomLineColor) - - // timeline - newStyle.timeline.backgroundColor = UIColor.useForStyle(dark: .black, white: newStyle.timeline.backgroundColor) - newStyle.timeline.timeColor = UIColor.useForStyle(dark: .systemGray, white: newStyle.timeline.timeColor) - newStyle.timeline.currentLineHourColor = UIColor.useForStyle(dark: .systemRed, - white: newStyle.timeline.currentLineHourColor) - - // week - newStyle.week.colorBackground = UIColor.useForStyle(dark: .black, white: newStyle.week.colorBackground) - newStyle.week.colorDate = UIColor.useForStyle(dark: .white, white: newStyle.week.colorDate) - newStyle.week.colorNameDay = UIColor.useForStyle(dark: .systemGray, white: newStyle.week.colorNameDay) - newStyle.week.colorCurrentDate = UIColor.useForStyle(dark: .systemGray, white: newStyle.week.colorCurrentDate) - newStyle.week.colorBackgroundSelectDate = UIColor.useForStyle(dark: .systemGray, - white: newStyle.week.colorBackgroundSelectDate) - newStyle.week.colorBackgroundCurrentDate = UIColor.useForStyle(dark: .systemGray, - white: newStyle.week.colorBackgroundCurrentDate) - newStyle.week.colorSelectDate = UIColor.useForStyle(dark: .white, white: newStyle.week.colorSelectDate) - newStyle.week.colorWeekendDate = UIColor.useForStyle(dark: .systemGray2, white: newStyle.week.colorWeekendDate) - newStyle.week.colorWeekendBackground = UIColor.useForStyle(dark: .clear, white: newStyle.week.colorWeekendBackground) - newStyle.week.colorWeekdayBackground = UIColor.useForStyle(dark: .clear, white: newStyle.week.colorWeekdayBackground) - - // month - newStyle.month.colorDate = UIColor.useForStyle(dark: .white, white: newStyle.month.colorDate) - newStyle.month.colorNameEmptyDay = UIColor.useForStyle(dark: .systemGray6, white: newStyle.month.colorNameEmptyDay) - newStyle.month.colorCurrentDate = UIColor.useForStyle(dark: .white, white: newStyle.month.colorCurrentDate) - newStyle.month.colorBackgroundCurrentDate = UIColor.useForStyle(dark: .systemRed, - white: newStyle.month.colorBackgroundCurrentDate) - newStyle.month.colorBackgroundSelectDate = UIColor.useForStyle(dark: .white, - white: newStyle.month.colorBackgroundSelectDate) - newStyle.month.colorSelectDate = UIColor.useForStyle(dark: .black, white: newStyle.month.colorSelectDate) - newStyle.month.colorWeekendDate = UIColor.useForStyle(dark: .systemGray2, white: newStyle.month.colorWeekendDate) - newStyle.month.colorMoreTitle = UIColor.useForStyle(dark: .systemGray3, white: newStyle.month.colorMoreTitle) - newStyle.month.colorEventTitle = UIColor.useForStyle(dark: .systemGray, white: newStyle.month.colorEventTitle) - if Platform.currentInterface == .phone { - newStyle.month.colorSeparator = UIColor.useForStyle(dark: .systemGray4, white: newStyle.month.colorSeparator) - newStyle.month.colorBackgroundWeekendDate = UIColor.useForStyle(dark: .black, - white: newStyle.month.colorBackgroundWeekendDate) - } else { - newStyle.month.colorSeparator = UIColor.useForStyle(dark: .systemGray, white: newStyle.month.colorSeparator) - newStyle.month.colorBackgroundWeekendDate = UIColor.useForStyle(dark: .systemGray6, - white: newStyle.month.colorBackgroundWeekendDate) - } - newStyle.month.colorBackgroundDate = UIColor.useForStyle(dark: .black, white: newStyle.month.colorBackgroundDate) - newStyle.month.colorTitleHeader = UIColor.useForStyle(dark: .white, white: newStyle.month.colorTitleHeader) - newStyle.month.colorBackground = UIColor.useForStyle(dark: .black, white: newStyle.month.colorBackground) - newStyle.month.colorTitleCurrentDate = .useForStyle(dark: .systemRed, white: newStyle.month.colorTitleCurrentDate) - - // year - newStyle.year.colorCurrentDate = UIColor.useForStyle(dark: .white, white: newStyle.year.colorCurrentDate) - newStyle.year.colorBackgroundCurrentDate = UIColor.useForStyle(dark: .systemRed, - white: newStyle.year.colorBackgroundCurrentDate) - newStyle.year.colorBackgroundSelectDate = UIColor.useForStyle(dark: .systemGray, - white: newStyle.year.colorBackgroundSelectDate) - newStyle.year.colorSelectDate = UIColor.useForStyle(dark: .white, white: newStyle.year.colorSelectDate) - newStyle.year.colorWeekendDate = UIColor.useForStyle(dark: .systemGray2, white: newStyle.year.colorWeekendDate) - newStyle.year.colorBackgroundWeekendDate = UIColor.useForStyle(dark: .clear, - white: newStyle.year.colorBackgroundWeekendDate) - newStyle.year.colorTitle = UIColor.useForStyle(dark: .white, white: newStyle.year.colorTitle) - newStyle.year.colorBackgroundHeader = UIColor.useForStyle(dark: .black, white: newStyle.year.colorBackgroundHeader) - newStyle.year.colorTitleHeader = UIColor.useForStyle(dark: .white, white: newStyle.year.colorTitleHeader) - newStyle.year.colorDayTitle = UIColor.useForStyle(dark: .systemGray, white: newStyle.year.colorDayTitle) - newStyle.year.colorBackground = UIColor.useForStyle(dark: .black, white: newStyle.year.colorBackground) - - // all day - newStyle.allDay.backgroundColor = UIColor.useForStyle(dark: .systemGray6, white: newStyle.allDay.backgroundColor) - newStyle.allDay.titleColor = UIColor.useForStyle(dark: .white, white: newStyle.allDay.titleColor) - newStyle.allDay.textColor = UIColor.useForStyle(dark: .white, white: newStyle.allDay.textColor) - - // list view - newStyle.list.backgroundColor = UIColor.useForStyle(dark: .black, white: newStyle.list.backgroundColor) + // event + newStyle.event.colorIconFile = UIColor.useForStyle(dark: .systemGray, white: newStyle.event.colorIconFile) + + // header + newStyle.headerScroll.colorNameEmptyDay = UIColor.useForStyle(dark: .systemGray6, + white: newStyle.headerScroll.colorNameEmptyDay) + newStyle.headerScroll.colorBackground = UIColor.useForStyle(dark: .black, white: newStyle.headerScroll.colorBackground) + newStyle.headerScroll.titleDateColor = UIColor.useForStyle(dark: .white, white: newStyle.headerScroll.titleDateColor) + newStyle.headerScroll.titleDateColorCorner = UIColor.useForStyle(dark: .systemRed, + white: newStyle.headerScroll.titleDateColorCorner) + newStyle.headerScroll.colorDate = UIColor.useForStyle(dark: .white, white: newStyle.headerScroll.colorDate) + newStyle.headerScroll.colorNameDay = UIColor.useForStyle(dark: .white, white: newStyle.headerScroll.colorNameDay) + newStyle.headerScroll.colorCurrentDate = UIColor.useForStyle(dark: .systemGray6, + white: newStyle.headerScroll.colorCurrentDate) + newStyle.headerScroll.colorBackgroundCurrentDate = UIColor.useForStyle(dark: .systemRed, + white: newStyle.headerScroll.colorBackgroundCurrentDate) + newStyle.headerScroll.colorBackgroundSelectDate = UIColor.useForStyle(dark: .white, + white: newStyle.headerScroll.colorBackgroundSelectDate) + newStyle.headerScroll.colorSelectDate = UIColor.useForStyle(dark: .black, white: newStyle.headerScroll.colorSelectDate) + newStyle.headerScroll.colorCurrentSelectDateForDarkStyle = UIColor.useForStyle(dark: .white, + white: newStyle.headerScroll.colorCurrentSelectDateForDarkStyle) + newStyle.headerScroll.colorWeekendDate = UIColor.useForStyle(dark: .systemGray2, + white: newStyle.headerScroll.colorWeekendDate) + newStyle.headerScroll.bottomLineColor = UIColor.useForStyle(dark: .systemGray2, + white: newStyle.headerScroll.bottomLineColor) + + // timeline + newStyle.timeline.backgroundColor = UIColor.useForStyle(dark: .black, white: newStyle.timeline.backgroundColor) + newStyle.timeline.timeColor = UIColor.useForStyle(dark: .systemGray, white: newStyle.timeline.timeColor) + newStyle.timeline.currentLineHourColor = UIColor.useForStyle(dark: .systemRed, + white: newStyle.timeline.currentLineHourColor) + + // week + newStyle.week.colorBackground = UIColor.useForStyle(dark: .black, white: newStyle.week.colorBackground) + newStyle.week.colorDate = UIColor.useForStyle(dark: .white, white: newStyle.week.colorDate) + newStyle.week.colorNameDay = UIColor.useForStyle(dark: .systemGray, white: newStyle.week.colorNameDay) + newStyle.week.colorCurrentDate = UIColor.useForStyle(dark: .systemGray, white: newStyle.week.colorCurrentDate) + newStyle.week.colorBackgroundSelectDate = UIColor.useForStyle(dark: .systemGray, + white: newStyle.week.colorBackgroundSelectDate) + newStyle.week.colorBackgroundCurrentDate = UIColor.useForStyle(dark: .systemGray, + white: newStyle.week.colorBackgroundCurrentDate) + newStyle.week.colorSelectDate = UIColor.useForStyle(dark: .white, white: newStyle.week.colorSelectDate) + newStyle.week.colorWeekendDate = UIColor.useForStyle(dark: .systemGray2, white: newStyle.week.colorWeekendDate) + newStyle.week.colorWeekendBackground = UIColor.useForStyle(dark: .clear, white: newStyle.week.colorWeekendBackground) + newStyle.week.colorWeekdayBackground = UIColor.useForStyle(dark: .clear, white: newStyle.week.colorWeekdayBackground) + + // month + newStyle.month.colorDate = UIColor.useForStyle(dark: .white, white: newStyle.month.colorDate) + newStyle.month.colorNameEmptyDay = UIColor.useForStyle(dark: .systemGray6, white: newStyle.month.colorNameEmptyDay) + newStyle.month.colorCurrentDate = UIColor.useForStyle(dark: .white, white: newStyle.month.colorCurrentDate) + newStyle.month.colorBackgroundCurrentDate = UIColor.useForStyle(dark: .systemRed, + white: newStyle.month.colorBackgroundCurrentDate) + newStyle.month.colorBackgroundSelectDate = UIColor.useForStyle(dark: .white, + white: newStyle.month.colorBackgroundSelectDate) + newStyle.month.colorSelectDate = UIColor.useForStyle(dark: .black, white: newStyle.month.colorSelectDate) + newStyle.month.colorWeekendDate = UIColor.useForStyle(dark: .systemGray2, white: newStyle.month.colorWeekendDate) + newStyle.month.colorMoreTitle = UIColor.useForStyle(dark: .systemGray3, white: newStyle.month.colorMoreTitle) + newStyle.month.colorEventTitle = UIColor.useForStyle(dark: .systemGray, white: newStyle.month.colorEventTitle) + if Platform.currentInterface == .phone { + newStyle.month.colorSeparator = UIColor.useForStyle(dark: .systemGray4, white: newStyle.month.colorSeparator) + newStyle.month.colorBackgroundWeekendDate = UIColor.useForStyle(dark: .black, + white: newStyle.month.colorBackgroundWeekendDate) + } else { + newStyle.month.colorSeparator = UIColor.useForStyle(dark: .systemGray, white: newStyle.month.colorSeparator) + newStyle.month.colorBackgroundWeekendDate = UIColor.useForStyle(dark: .systemGray6, + white: newStyle.month.colorBackgroundWeekendDate) } + newStyle.month.colorBackgroundDate = UIColor.useForStyle(dark: .black, white: newStyle.month.colorBackgroundDate) + newStyle.month.colorTitleHeader = UIColor.useForStyle(dark: .white, white: newStyle.month.colorTitleHeader) + newStyle.month.colorBackground = UIColor.useForStyle(dark: .black, white: newStyle.month.colorBackground) + newStyle.month.colorTitleCurrentDate = .useForStyle(dark: .systemRed, white: newStyle.month.colorTitleCurrentDate) + + // year + newStyle.year.colorCurrentDate = UIColor.useForStyle(dark: .white, white: newStyle.year.colorCurrentDate) + newStyle.year.colorBackgroundCurrentDate = UIColor.useForStyle(dark: .systemRed, + white: newStyle.year.colorBackgroundCurrentDate) + newStyle.year.colorBackgroundSelectDate = UIColor.useForStyle(dark: .systemGray, + white: newStyle.year.colorBackgroundSelectDate) + newStyle.year.colorSelectDate = UIColor.useForStyle(dark: .white, white: newStyle.year.colorSelectDate) + newStyle.year.colorWeekendDate = UIColor.useForStyle(dark: .systemGray2, white: newStyle.year.colorWeekendDate) + newStyle.year.colorBackgroundWeekendDate = UIColor.useForStyle(dark: .clear, + white: newStyle.year.colorBackgroundWeekendDate) + newStyle.year.colorTitle = UIColor.useForStyle(dark: .white, white: newStyle.year.colorTitle) + newStyle.year.colorBackgroundHeader = UIColor.useForStyle(dark: .black, white: newStyle.year.colorBackgroundHeader) + newStyle.year.colorTitleHeader = UIColor.useForStyle(dark: .white, white: newStyle.year.colorTitleHeader) + newStyle.year.colorDayTitle = UIColor.useForStyle(dark: .systemGray, white: newStyle.year.colorDayTitle) + newStyle.year.colorBackground = UIColor.useForStyle(dark: .black, white: newStyle.year.colorBackground) + + // all day + newStyle.allDay.backgroundColor = UIColor.useForStyle(dark: .systemGray6, white: newStyle.allDay.backgroundColor) + newStyle.allDay.titleColor = UIColor.useForStyle(dark: .white, white: newStyle.allDay.titleColor) + newStyle.allDay.textColor = UIColor.useForStyle(dark: .white, white: newStyle.allDay.textColor) + + // list view + newStyle.list.backgroundColor = UIColor.useForStyle(dark: .black, white: newStyle.list.backgroundColor) return newStyle } } diff --git a/Sources/KVKCalendar/SwiftUI+Extensions.swift b/Sources/KVKCalendar/SwiftUI+Extensions.swift new file mode 100644 index 00000000..e87405b2 --- /dev/null +++ b/Sources/KVKCalendar/SwiftUI+Extensions.swift @@ -0,0 +1,124 @@ +// +// SwiftUI+Extensions.swift +// KVKCalendar +// +// Created by Sergei Kviatkovskii on 11/16/22. +// + +import SwiftUI + +struct DeviceRotationViewModifier: ViewModifier { + + let action: (UIInterfaceOrientation) -> Void + + func body(content: Content) -> some View { + content + .onAppear() + .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { (_) in + action(UIApplication.shared.orientation) + } + } + +} + +public extension View { + + func kvkOnRotate(action: @escaping (UIInterfaceOrientation) -> Void) -> some View { + modifier(DeviceRotationViewModifier(action: action)) + } + + func kvkHandleNavigationView(_ view: some View) -> some View { + if #available(iOS 16.0, *) { + return NavigationStack { + view + } + } else { + return NavigationView { + view + } + .navigationViewStyle(StackNavigationViewStyle()) + } + } + +} + +public protocol ItemsMenuProxy: Identifiable, Equatable { + + var title: String { get } + +} + +@available(iOS 14.0, *) +public struct ItemsMenu: View { + + @Binding var type: T + @State var items: [T] + private var showCheckmark: Bool + private var color: Color + private var showDropDownIcon: Bool + + public init(type: Binding, + items: [T], + showCheckmark: Bool = false, + color: Color = .red, + showDropDownIcon: Bool = false) { + _type = type + _items = State(initialValue: items) + self.showCheckmark = showCheckmark + self.color = color + self.showDropDownIcon = showDropDownIcon + } + + public var body: some View { + Menu { + ForEach(items) { (btn) in + Button { + type = btn + } label: { + HStack { + if type == btn && showCheckmark { + Image(systemName: "checkmark") + } + Text(btn.title) + } + } + } + } label: { + HStack { + Text(type.title) + .foregroundColor(color) + .font(.headline) + if showDropDownIcon { + Image(systemName: "chevron.down") + .resizable() + .frame(width: 12, height: 7) + .foregroundColor(color) + .padding(EdgeInsets(top: 5, leading: 0, bottom: 0, trailing: 0)) + } + } + } + } + +} + +@available(iOS 14.0, *) +struct ItemsMenu_Previews: PreviewProvider { + static var previews: some View { + StatefulPreviewWrapper(CalendarType.day) { ItemsMenu(type: $0, items: CalendarType.allCases.reversed(), showCheckmark: true, showDropDownIcon: true) } + } +} + +public struct StatefulPreviewWrapper: View { + @State var value: Value + var content: (Binding) -> Content + + public var body: some View { + content($value) + } + + public init(_ value: Value, + content: @escaping (Binding) -> Content) { + self._value = State(wrappedValue: value) + self.content = content + } +} diff --git a/Sources/KVKCalendar/Timeline+Extension.swift b/Sources/KVKCalendar/Timeline+Extension.swift index f4e2d858..15d598e6 100644 --- a/Sources/KVKCalendar/Timeline+Extension.swift +++ b/Sources/KVKCalendar/Timeline+Extension.swift @@ -325,7 +325,7 @@ extension TimelineView { var otherTimes = [UILabel]() for (idx, txtHour) in timeSystem.hours.enumerated() where idx >= start { let yTime = (calculatedTimeY + style.timeline.heightTime) * CGFloat(idx - start) - let time = TimelineLabel(frame: CGRect(x: style.timeline.offsetTimeX + style.timeline.cornerHeaderWidth, + let time = TimelineLabel(frame: CGRect(x: leftOffsetWithAdditionalTime, y: yTime, width: style.timeline.widthTime, height: style.timeline.heightTime)) @@ -339,7 +339,7 @@ extension TimelineView { time.tag = idx - start time.isHidden = !isDisplayedTimes - if let item = dataSource?.dequeueTimeLabel(time) { + if let item = dataSource?.dequeueTimeLabel(time) ?? delegate?.dequeueTimeLabel(time) { otherTimes += item.others times.append(item.current) } else { @@ -353,11 +353,9 @@ extension TimelineView { times.enumerated().reduce([]) { acc, item -> [UIView] in let time = item.element let idx = item.offset - - let xLine = time.frame.width + style.timeline.offsetTimeX + style.timeline.offsetLineLeft + style.timeline.cornerHeaderWidth - let lineFrame = CGRect(x: xLine, + let lineFrame = CGRect(x: leftOffsetWithAdditionalTime, y: time.center.y, - width: frame.width - xLine, + width: frame.width - leftOffsetWithAdditionalTime, height: style.timeline.heightLine) let line = UIView(frame: lineFrame) line.backgroundColor = style.timeline.separatorLineColor @@ -654,21 +652,21 @@ extension TimelineView: EventDelegate { location.y = (location.y - eventPreviewYOffset) - style.timeline.offsetEvent - 6 let startTime = movingMinuteLabel.time if !event.isNew { - var newDayEvent: Int? + var newDateEvent: Date? var updatedEvent = event - if paramaters.type == .week, let newDate = shadowView.date { - newDayEvent = newDate.kvkDay + if paramaters.type == .week, let shadowDate = shadowView.date { + newDateEvent = shadowDate if event.recurringType != .none { - updatedEvent = event.updateDate(newDate: newDate, calendar: style.calendar) ?? event + updatedEvent = event.updateDate(newDate: shadowDate, calendar: style.calendar) ?? event } } delegate?.didChangeEvent(updatedEvent, minute: startTime.minute, hour: startTime.hour, point: location, - newDay: newDayEvent) + newDate: newDateEvent) } shadowView.removeFromSuperview() @@ -711,7 +709,7 @@ extension TimelineView: EventDelegate { let time = calculateChangingTime(pointY: pointTempY) if let minute = time.minute, 0...59 ~= minute { - movingMinuteLabel.frame = CGRect(x: style.timeline.offsetTimeX + style.timeline.cornerHeaderWidth, + movingMinuteLabel.frame = CGRect(x: leftOffsetWithAdditionalTime, y: (pointY - offset) - style.timeline.heightTime, width: style.timeline.widthTime, height: style.timeline.heightTime) scrollView.addSubview(movingMinuteLabel) @@ -760,7 +758,7 @@ extension TimelineView: CalendarSettingProtocol { } func setUI(reload: Bool = false) { - currentLineView.frame.origin.x = timeLabels.first?.frame.origin.x ?? style.timeline.cornerHeaderWidth + currentLineView.frame.origin.x = timeLabels.first?.frame.origin.x ?? (leftOffsetWithAdditionalTime - style.timeline.widthTime) scrollView.backgroundColor = style.timeline.backgroundColor scrollView.isScrollEnabled = style.timeline.scrollDirections.contains(.vertical) diff --git a/Sources/KVKCalendar/TimelineModel.swift b/Sources/KVKCalendar/TimelineModel.swift index 73d61237..ac4ae68f 100644 --- a/Sources/KVKCalendar/TimelineModel.swift +++ b/Sources/KVKCalendar/TimelineModel.swift @@ -22,9 +22,10 @@ protocol TimelineDelegate: AnyObject { func nextDate() func previousDate() func swipeX(transform: CGAffineTransform, stop: Bool) - func didChangeEvent(_ event: Event, minute: Int, hour: Int, point: CGPoint, newDay: Int?) + func didChangeEvent(_ event: Event, minute: Int, hour: Int, point: CGPoint, newDate: Date?) func didAddNewEvent(_ event: Event, minute: Int, hour: Int, point: CGPoint) func didResizeEvent(_ event: Event, startTime: ResizeTime, endTime: ResizeTime) + func dequeueTimeLabel(_ label: TimelineLabel) -> (current: TimelineLabel, others: [UILabel])? } extension TimelineDelegate { diff --git a/Sources/KVKCalendar/TimelineView.swift b/Sources/KVKCalendar/TimelineView.swift index f76e66d7..351333de 100644 --- a/Sources/KVKCalendar/TimelineView.swift +++ b/Sources/KVKCalendar/TimelineView.swift @@ -48,10 +48,7 @@ final class TimelineView: UIView, EventDateProtocol, CalendarTimer { }() var calculatedCurrentLineViewFrame: CGRect { - var currentLineFrame = frame - currentLineFrame.origin = timeLabels.first?.frame.origin ?? CGPoint(x: style.timeline.currentLineHourWidth, - y: 0) - return currentLineFrame + frame } private(set) var tagCurrentHourLine = -10 @@ -82,7 +79,7 @@ final class TimelineView: UIView, EventDateProtocol, CalendarTimer { let label = TimelineLabel() label.adjustsFontSizeToFitWidth = true label.textColor = style.timeline.movingMinutesColor - label.textAlignment = .right + label.textAlignment = .left label.font = style.timeline.timeFont label.isHidden = !isDisplayedMovingTime return label @@ -182,6 +179,7 @@ final class TimelineView: UIView, EventDateProtocol, CalendarTimer { currentLineView.reloadFrame(calculatedCurrentLineViewFrame) currentLineView.updateStyle(style, force: true) + currentLineView.setOffsetForTime(timeLabels.first?.frame.origin.x ?? 0) let pointY = calculatePointYByMinute(date.kvkMinute, time: time) currentLineView.frame.origin.y = pointY - (currentLineView.frame.height * 0.5) scrollView.addSubview(currentLineView) @@ -252,6 +250,23 @@ final class TimelineView: UIView, EventDateProtocol, CalendarTimer { scrollView.scrollRectToVisible(frame, animated: true) } + private typealias EventOrder = (Event, Event) -> Bool + private func sortedEvent(_ lhs: Event, + rhs: Event) -> Bool { + let predicates: [EventOrder] = [ + { $0.style?.defaultWidth != nil && !($1.style?.defaultWidth != nil) }, + { $0.start.kvkHour < $1.start.kvkHour } + ] + + for predicate in predicates { + if !predicate(lhs, rhs) && !predicate(rhs, lhs) { + continue + } + return predicate(lhs, rhs) + } + return false + } + func create(dates: [Date], events: [Event], recurringEvents: [Event], selectedDate: Date) { isResizableEventEnable = false delegate?.didDisplayEvents(events, dates: dates) @@ -279,12 +294,14 @@ final class TimelineView: UIView, EventDateProtocol, CalendarTimer { startHour = style.timeline.startHour } else { if dates.count > 1 { - startHour = filteredEvents.sorted(by: { $0.start.kvkHour < $1.start.kvkHour }) + startHour = filteredEvents + .sorted(by: { $0.start.kvkHour < $1.start.kvkHour }) .first?.start.kvkHour ?? style.timeline.startHour } else { - startHour = filteredEvents.filter { compareStartDate(selectedDate, with: $0) } - .sorted(by: { $0.start.kvkHour < $1.start.kvkHour }) - .first?.start.kvkHour ?? style.timeline.startHour + startHour = filteredEvents + .filter { compareStartDate(selectedDate, with: $0) } + .sorted(by: { $0.start.kvkHour < $1.start.kvkHour }) + .first?.start.kvkHour ?? style.timeline.startHour } } @@ -298,7 +315,7 @@ final class TimelineView: UIView, EventDateProtocol, CalendarTimer { labels.items.forEach { scrollView.addSubview($0) } horizontalLines.forEach { scrollView.addSubview($0) } - let leftOffset = style.timeline.widthTime + style.timeline.offsetTimeX + style.timeline.offsetLineLeft + style.timeline.cornerHeaderWidth + let leftOffset = leftOffsetWithAdditionalTime let widthPage = (frame.width - leftOffset) / CGFloat(dates.count) let heightPage = scrollView.contentSize.height var allDayEvents = [AllDayView.PrepareEvents]() @@ -317,11 +334,13 @@ final class TimelineView: UIView, EventDateProtocol, CalendarTimer { let verticalLine = createVerticalLine(pointX: pointX, date: date) layer.addSublayer(verticalLine) - let eventsByDate = filteredEvents.filter { - compareStartDate(date, with: $0) - || compareEndDate(date, with: $0) - || checkMultipleDate(date, with: $0) - } + let eventsByDate = filteredEvents + .filter { + compareStartDate(date, with: $0) + || compareEndDate(date, with: $0) + || checkMultipleDate(date, with: $0) + } + .sorted(by: sortedEvent(_:rhs:)) let allDayEventsForDate = filteredAllDayEvents.filter { compareStartDate(date, with: $0) || compareEndDate(date, with: $0) diff --git a/Sources/KVKCalendar/WeekView.swift b/Sources/KVKCalendar/WeekView.swift index 35baba3a..743f4a0f 100644 --- a/Sources/KVKCalendar/WeekView.swift +++ b/Sources/KVKCalendar/WeekView.swift @@ -173,7 +173,7 @@ extension WeekView: CalendarSettingProtocol { guard let self = self else { return } let newTimeline = self.createTimelineView(frame: self.timelinePage.bounds) - + newTimeline.updateStyle(self.style, force: reload) switch type { case .next: self.nextDate() @@ -306,6 +306,11 @@ extension WeekView: CalendarSettingProtocol { self.timelinePage.addNewTimelineView(newTimeline, to: .begin) } } + view.didUpdateStyle = { [weak self] (type) in + guard let self = self else { return } + + self.delegate?.didUpdateStyle(self.scrollableWeekView.style, type: type) + } return view } @@ -365,17 +370,23 @@ extension WeekView: TimelineDelegate { delegate?.didAddNewEvent(event, newDate) } - func didChangeEvent(_ event: Event, minute: Int, hour: Int, point: CGPoint, newDay: Int?) { + func didChangeEvent(_ event: Event, minute: Int, hour: Int, point: CGPoint, newDate: Date?) { var day = event.start.kvkDay - if let newDayEvent = newDay { - day = newDayEvent + var month = event.start.kvkMonth + var year = event.start.kvkYear + if let newDayEvent = newDate { + day = newDayEvent.kvkDay + month = newDayEvent.kvkMonth + year = newDayEvent.kvkYear } else if let newDate = scrollableWeekView.getDateByPointX(point.x), day != newDate.kvkDay { day = newDate.kvkDay + month = newDate.kvkMonth + year = newDate.kvkYear } var startComponents = DateComponents() - startComponents.year = event.start.kvkYear - startComponents.month = event.start.kvkMonth + startComponents.year = year + startComponents.month = month startComponents.day = day startComponents.hour = hour startComponents.minute = minute @@ -384,20 +395,38 @@ extension WeekView: TimelineDelegate { let hourOffset = event.end.kvkHour - event.start.kvkHour let minuteOffset = event.end.kvkMinute - event.start.kvkMinute var endComponents = DateComponents() - endComponents.year = event.end.kvkYear - endComponents.month = event.end.kvkMonth + + if event.end.kvkYear != event.start.kvkYear { + let offset = event.end.kvkYear - event.start.kvkYear + endComponents.year = year + offset + } else { + endComponents.year = year + } + + if event.end.kvkMonth != event.start.kvkMonth { + let offset = event.end.kvkMonth - event.start.kvkMonth + endComponents.month = month + offset + } else { + endComponents.month = month + } + if event.end.kvkDay != event.start.kvkDay { let offset = event.end.kvkDay - event.start.kvkDay endComponents.day = day + offset } else { endComponents.day = day } + endComponents.hour = hour + hourOffset endComponents.minute = minute + minuteOffset let endDate = style.calendar.date(from: endComponents) delegate?.didChangeEvent(event, start: startDate, end: endDate) } + func dequeueTimeLabel(_ label: TimelineLabel) -> (current: TimelineLabel, others: [UILabel])? { + handleTimelineLabel(zones: style.selectedTimeZones, label: label) + } + } #endif diff --git a/Sources/KVKCalendar/YearCell.swift b/Sources/KVKCalendar/YearCell.swift index c621a52d..27d20371 100644 --- a/Sources/KVKCalendar/YearCell.swift +++ b/Sources/KVKCalendar/YearCell.swift @@ -92,7 +92,7 @@ final class YearCell: UICollectionViewCell { addSubview(titleLabel) if #available(iOS 13.4, *) { - addPointInteraction(on: self, delegate: self) + addPointInteraction() } } @@ -202,19 +202,4 @@ final class YearCell: UICollectionViewCell { } } -@available(iOS 13.4, *) -extension YearCell: PointerInteractionProtocol { - - func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? { - var pointerStyle: UIPointerStyle? - - if let interactionView = interaction.view { - let targetedPreview = UITargetedPreview(view: interactionView) - pointerStyle = UIPointerStyle(effect: .hover(targetedPreview)) - } - return pointerStyle - } - -} - #endif