Adding/removing/editing events
This commit is contained in:
parent
3332d12b05
commit
734fc5321f
@ -77,6 +77,10 @@
|
|||||||
7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */; };
|
7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */; };
|
||||||
7A813DBE2506A57100CC93B9 /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A813DBD2506A57100CC93B9 /* AuthenticationServices.framework */; };
|
7A813DBE2506A57100CC93B9 /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A813DBD2506A57100CC93B9 /* AuthenticationServices.framework */; };
|
||||||
7A813DC12508C4D900CC93B9 /* ExceptionCatcher in Frameworks */ = {isa = PBXBuildFile; productRef = 7A813DC02508C4D900CC93B9 /* ExceptionCatcher */; };
|
7A813DC12508C4D900CC93B9 /* ExceptionCatcher in Frameworks */ = {isa = PBXBuildFile; productRef = 7A813DC02508C4D900CC93B9 /* ExceptionCatcher */; };
|
||||||
|
7A813DC32508EE4F00CC93B9 /* EventCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A813DC22508EE4F00CC93B9 /* EventCell.swift */; };
|
||||||
|
7A813DC5250AAF3C00CC93B9 /* LocationEditController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A813DC4250AAF3C00CC93B9 /* LocationEditController.swift */; };
|
||||||
|
7A813DC9250B5C9700CC93B9 /* LocationRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A813DC8250B5C9700CC93B9 /* LocationRow.swift */; };
|
||||||
|
7A813DCB250B5DC900CC93B9 /* LocationPickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A813DCA250B5DC900CC93B9 /* LocationPickerController.swift */; };
|
||||||
7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */; };
|
7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */; };
|
||||||
7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */; };
|
7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */; };
|
||||||
7A96AE2A246AFD6200297C33 /* Eureka in Frameworks */ = {isa = PBXBuildFile; productRef = 7A96AE29246AFD6200297C33 /* Eureka */; };
|
7A96AE2A246AFD6200297C33 /* Eureka in Frameworks */ = {isa = PBXBuildFile; productRef = 7A96AE29246AFD6200297C33 /* Eureka */; };
|
||||||
@ -88,6 +92,7 @@
|
|||||||
7AB562BA249C9E9B00473D53 /* Region.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB562B9249C9E9B00473D53 /* Region.swift */; };
|
7AB562BA249C9E9B00473D53 /* Region.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB562B9249C9E9B00473D53 /* Region.swift */; };
|
||||||
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8B2435C38700258F61 /* CustomTextField.swift */; };
|
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8B2435C38700258F61 /* CustomTextField.swift */; };
|
||||||
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8D2435D1A000258F61 /* CustomButton.swift */; };
|
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8D2435D1A000258F61 /* CustomButton.swift */; };
|
||||||
|
7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C92250B954900F237B2 /* Navigation.swift */; };
|
||||||
7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */; };
|
7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */; };
|
||||||
7AE26A3524F31B0700625033 /* EventsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3424F31B0700625033 /* EventsController.swift */; };
|
7AE26A3524F31B0700625033 /* EventsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3424F31B0700625033 /* EventsController.swift */; };
|
||||||
7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFE727240455E200910EB7 /* SettingsController.swift */; };
|
7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFE727240455E200910EB7 /* SettingsController.swift */; };
|
||||||
@ -160,6 +165,10 @@
|
|||||||
7A7547DC2403180A004E8406 /* SectionHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SectionHeader.xib; sourceTree = "<group>"; };
|
7A7547DC2403180A004E8406 /* SectionHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SectionHeader.xib; sourceTree = "<group>"; };
|
||||||
7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehiclePhotoCell.swift; sourceTree = "<group>"; };
|
7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehiclePhotoCell.swift; sourceTree = "<group>"; };
|
||||||
7A813DBD2506A57100CC93B9 /* AuthenticationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AuthenticationServices.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/AuthenticationServices.framework; sourceTree = DEVELOPER_DIR; };
|
7A813DBD2506A57100CC93B9 /* AuthenticationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AuthenticationServices.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/AuthenticationServices.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
|
7A813DC22508EE4F00CC93B9 /* EventCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventCell.swift; sourceTree = "<group>"; };
|
||||||
|
7A813DC4250AAF3C00CC93B9 /* LocationEditController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditController.swift; sourceTree = "<group>"; };
|
||||||
|
7A813DC8250B5C9700CC93B9 /* LocationRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRow.swift; sourceTree = "<group>"; };
|
||||||
|
7A813DCA250B5DC900CC93B9 /* LocationPickerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPickerController.swift; sourceTree = "<group>"; };
|
||||||
7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeImage.swift; sourceTree = "<group>"; };
|
7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeImage.swift; sourceTree = "<group>"; };
|
||||||
7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleReportImage.swift; sourceTree = "<group>"; };
|
7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleReportImage.swift; sourceTree = "<group>"; };
|
||||||
7A92D0AB240425B100EF3B77 /* ATGMediaBrowser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ATGMediaBrowser.framework; path = Carthage/Build/iOS/ATGMediaBrowser.framework; sourceTree = "<group>"; };
|
7A92D0AB240425B100EF3B77 /* ATGMediaBrowser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ATGMediaBrowser.framework; path = Carthage/Build/iOS/ATGMediaBrowser.framework; sourceTree = "<group>"; };
|
||||||
@ -171,6 +180,7 @@
|
|||||||
7AB562B9249C9E9B00473D53 /* Region.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Region.swift; sourceTree = "<group>"; };
|
7AB562B9249C9E9B00473D53 /* Region.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Region.swift; sourceTree = "<group>"; };
|
||||||
7AB67E8B2435C38700258F61 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = "<group>"; };
|
7AB67E8B2435C38700258F61 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = "<group>"; };
|
||||||
7AB67E8D2435D1A000258F61 /* CustomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButton.swift; sourceTree = "<group>"; };
|
7AB67E8D2435D1A000258F61 /* CustomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButton.swift; sourceTree = "<group>"; };
|
||||||
|
7ADF6C92250B954900F237B2 /* Navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Navigation.swift; sourceTree = "<group>"; };
|
||||||
7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerExt.swift; sourceTree = "<group>"; };
|
7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerExt.swift; sourceTree = "<group>"; };
|
||||||
7AE26A3424F31B0700625033 /* EventsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsController.swift; sourceTree = "<group>"; };
|
7AE26A3424F31B0700625033 /* EventsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsController.swift; sourceTree = "<group>"; };
|
||||||
7AEFE727240455E200910EB7 /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = "<group>"; };
|
7AEFE727240455E200910EB7 /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = "<group>"; };
|
||||||
@ -248,6 +258,7 @@
|
|||||||
7A11471423FDEAF800B424AF /* Controllers */ = {
|
7A11471423FDEAF800B424AF /* Controllers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
7A813DC7250B5C6E00CC93B9 /* Location */,
|
||||||
7A11471523FDEB2A00B424AF /* MainSplitController.swift */,
|
7A11471523FDEB2A00B424AF /* MainSplitController.swift */,
|
||||||
7A11471723FDEBFA00B424AF /* ReportController.swift */,
|
7A11471723FDEBFA00B424AF /* ReportController.swift */,
|
||||||
7A11471923FE839000B424AF /* AuthController.swift */,
|
7A11471923FE839000B424AF /* AuthController.swift */,
|
||||||
@ -259,7 +270,6 @@
|
|||||||
7A33381024990DAE00D878F1 /* FiltersController.swift */,
|
7A33381024990DAE00D878F1 /* FiltersController.swift */,
|
||||||
7A27ADC6249D43210035F39E /* RegionsController.swift */,
|
7A27ADC6249D43210035F39E /* RegionsController.swift */,
|
||||||
7A27ADF2249F8B650035F39E /* RecordsController.swift */,
|
7A27ADF2249F8B650035F39E /* RecordsController.swift */,
|
||||||
7AE26A3424F31B0700625033 /* EventsController.swift */,
|
|
||||||
);
|
);
|
||||||
path = Controllers;
|
path = Controllers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -329,6 +339,7 @@
|
|||||||
7A659B5A24A3768A0043A0F2 /* Substrings.swift */,
|
7A659B5A24A3768A0043A0F2 /* Substrings.swift */,
|
||||||
7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */,
|
7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */,
|
||||||
7A21112924FC3D7E003BBF6F /* AudioEngine.swift */,
|
7A21112924FC3D7E003BBF6F /* AudioEngine.swift */,
|
||||||
|
7ADF6C92250B954900F237B2 /* Navigation.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -354,6 +365,7 @@
|
|||||||
7A7547DC2403180A004E8406 /* SectionHeader.xib */,
|
7A7547DC2403180A004E8406 /* SectionHeader.xib */,
|
||||||
7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */,
|
7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */,
|
||||||
7A1090E724A394F100B4F0B2 /* AudioRecordCell.swift */,
|
7A1090E724A394F100B4F0B2 /* AudioRecordCell.swift */,
|
||||||
|
7A813DC22508EE4F00CC93B9 /* EventCell.swift */,
|
||||||
);
|
);
|
||||||
path = Cells;
|
path = Cells;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -402,6 +414,17 @@
|
|||||||
path = Fonts;
|
path = Fonts;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
7A813DC7250B5C6E00CC93B9 /* Location */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
7A813DC4250AAF3C00CC93B9 /* LocationEditController.swift */,
|
||||||
|
7AE26A3424F31B0700625033 /* EventsController.swift */,
|
||||||
|
7A813DC8250B5C9700CC93B9 /* LocationRow.swift */,
|
||||||
|
7A813DCA250B5DC900CC93B9 /* LocationPickerController.swift */,
|
||||||
|
);
|
||||||
|
path = Location;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@ -512,7 +535,9 @@
|
|||||||
7A64AE822469E16100ABE48E /* IHProgressHUD.swift in Sources */,
|
7A64AE822469E16100ABE48E /* IHProgressHUD.swift in Sources */,
|
||||||
7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */,
|
7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */,
|
||||||
7A27ADF924A09CAD0035F39E /* CocoaError.swift in Sources */,
|
7A27ADF924A09CAD0035F39E /* CocoaError.swift in Sources */,
|
||||||
|
7A813DC9250B5C9700CC93B9 /* LocationRow.swift in Sources */,
|
||||||
7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */,
|
7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */,
|
||||||
|
7A813DC32508EE4F00CC93B9 /* EventCell.swift in Sources */,
|
||||||
7A3F07AD2436350B00E59687 /* SearchController.swift in Sources */,
|
7A3F07AD2436350B00E59687 /* SearchController.swift in Sources */,
|
||||||
7AB562BA249C9E9B00473D53 /* Region.swift in Sources */,
|
7AB562BA249C9E9B00473D53 /* Region.swift in Sources */,
|
||||||
7A659B5924A2B1BA0043A0F2 /* AudioRecord.swift in Sources */,
|
7A659B5924A2B1BA0043A0F2 /* AudioRecord.swift in Sources */,
|
||||||
@ -549,12 +574,14 @@
|
|||||||
7A11474723FF2AA500B424AF /* User.swift in Sources */,
|
7A11474723FF2AA500B424AF /* User.swift in Sources */,
|
||||||
7A11471623FDEB2A00B424AF /* MainSplitController.swift in Sources */,
|
7A11471623FDEB2A00B424AF /* MainSplitController.swift in Sources */,
|
||||||
7AF58D3124029E1000CE01A0 /* VehicleHeaderCell.swift in Sources */,
|
7AF58D3124029E1000CE01A0 /* VehicleHeaderCell.swift in Sources */,
|
||||||
|
7A813DC5250AAF3C00CC93B9 /* LocationEditController.swift in Sources */,
|
||||||
7A43F9F8246C8A6200BA5B49 /* JWT.swift in Sources */,
|
7A43F9F8246C8A6200BA5B49 /* JWT.swift in Sources */,
|
||||||
7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */,
|
7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */,
|
||||||
7A488C3F24A74B990054D0B2 /* RealmBindObserver.swift in Sources */,
|
7A488C3F24A74B990054D0B2 /* RealmBindObserver.swift in Sources */,
|
||||||
7AAE6AD324CDDF950023860B /* VehicleEvent.swift in Sources */,
|
7AAE6AD324CDDF950023860B /* VehicleEvent.swift in Sources */,
|
||||||
7A11470323FDE7E500B424AF /* SceneDelegate.swift in Sources */,
|
7A11470323FDE7E500B424AF /* SceneDelegate.swift in Sources */,
|
||||||
7A530B7E24017FEE00CBFE6E /* VehicleCell.swift in Sources */,
|
7A530B7E24017FEE00CBFE6E /* VehicleCell.swift in Sources */,
|
||||||
|
7A813DCB250B5DC900CC93B9 /* LocationPickerController.swift in Sources */,
|
||||||
7A11474423FF06CA00B424AF /* Api.swift in Sources */,
|
7A11474423FF06CA00B424AF /* Api.swift in Sources */,
|
||||||
7A488C3D24A74B990054D0B2 /* RxCollectionViewRealmDataSource.swift in Sources */,
|
7A488C3D24A74B990054D0B2 /* RxCollectionViewRealmDataSource.swift in Sources */,
|
||||||
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */,
|
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */,
|
||||||
@ -565,6 +592,7 @@
|
|||||||
7A05161A2414FF0900FC55AC /* DateSection.swift in Sources */,
|
7A05161A2414FF0900FC55AC /* DateSection.swift in Sources */,
|
||||||
7A333814249A532400D878F1 /* Filter.swift in Sources */,
|
7A333814249A532400D878F1 /* Filter.swift in Sources */,
|
||||||
7A11474B23FF368B00B424AF /* Settings.swift in Sources */,
|
7A11474B23FF368B00B424AF /* Settings.swift in Sources */,
|
||||||
|
7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */,
|
||||||
7A64AE752469DFB600ABE48E /* MediaBrowserViewController.swift in Sources */,
|
7A64AE752469DFB600ABE48E /* MediaBrowserViewController.swift in Sources */,
|
||||||
7A64AE732469DFB600ABE48E /* DismissAnimationController.swift in Sources */,
|
7A64AE732469DFB600ABE48E /* DismissAnimationController.swift in Sources */,
|
||||||
7A64AE812469E16100ABE48E /* ProgressAnimatedView.swift in Sources */,
|
7A64AE812469E16100ABE48E /* ProgressAnimatedView.swift in Sources */,
|
||||||
@ -715,7 +743,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 28;
|
CURRENT_PROJECT_VERSION = 29;
|
||||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
@ -737,7 +765,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 28;
|
CURRENT_PROJECT_VERSION = 29;
|
||||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
|
|||||||
@ -23,7 +23,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||||
|
|
||||||
let config = Realm.Configuration(
|
let config = Realm.Configuration(
|
||||||
schemaVersion: 15,
|
schemaVersion: 16,
|
||||||
migrationBlock: { migration, oldSchemaVersion in
|
migrationBlock: { migration, oldSchemaVersion in
|
||||||
if oldSchemaVersion <= 3 {
|
if oldSchemaVersion <= 3 {
|
||||||
var numbers: [String] = []
|
var numbers: [String] = []
|
||||||
|
|||||||
22
AutoCat/Assets.xcassets/MapPin.imageset/Contents.json
vendored
Normal file
22
AutoCat/Assets.xcassets/MapPin.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "map_pin@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "map_pin@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
AutoCat/Assets.xcassets/MapPin.imageset/map_pin@2x.png
vendored
Normal file
BIN
AutoCat/Assets.xcassets/MapPin.imageset/map_pin@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
BIN
AutoCat/Assets.xcassets/MapPin.imageset/map_pin@3x.png
vendored
Normal file
BIN
AutoCat/Assets.xcassets/MapPin.imageset/map_pin@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
@ -227,27 +227,94 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
|
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="ytQ-Th-luv">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<prototypes>
|
||||||
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="EventCell" id="QIb-Hv-tvk" customClass="EventCell" customModule="AutoCat" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="28" width="375" height="63"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="QIb-Hv-tvk" id="Ypt-ch-fGT">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="63"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="HP8-oO-yhP">
|
||||||
|
<rect key="frame" x="8" y="8" width="359" height="47"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xcQ-Wz-gJ0">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="359" height="20.5"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1tQ-zM-6T9">
|
||||||
|
<rect key="frame" x="0.0" y="28.5" width="359" height="18.5"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||||
|
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="HP8-oO-yhP" firstAttribute="leading" secondItem="Ypt-ch-fGT" secondAttribute="leading" constant="8" id="BX3-sO-2mI"/>
|
||||||
|
<constraint firstItem="HP8-oO-yhP" firstAttribute="top" secondItem="Ypt-ch-fGT" secondAttribute="top" constant="8" id="kUQ-n4-izs"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="HP8-oO-yhP" secondAttribute="trailing" constant="8" id="nGB-VN-Sdx"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="HP8-oO-yhP" secondAttribute="bottom" constant="8" id="pFY-Uy-LSv"/>
|
||||||
|
</constraints>
|
||||||
|
</tableViewCellContentView>
|
||||||
|
<connections>
|
||||||
|
<outlet property="address" destination="xcQ-Wz-gJ0" id="k6e-Yz-MEB"/>
|
||||||
|
<outlet property="date" destination="1tQ-zM-6T9" id="DBs-dL-CmY"/>
|
||||||
|
</connections>
|
||||||
|
</tableViewCell>
|
||||||
|
</prototypes>
|
||||||
|
<connections>
|
||||||
|
<outlet property="dataSource" destination="DmF-8j-ae3" id="ayM-aB-LZb"/>
|
||||||
|
<outlet property="delegate" destination="DmF-8j-ae3" id="O5y-AS-NK0"/>
|
||||||
|
</connections>
|
||||||
|
</tableView>
|
||||||
<mapView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" mapType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="3Ru-VE-szI">
|
<mapView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" mapType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="3Ru-VE-szI">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
</mapView>
|
</mapView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="bottomMargin" secondItem="3Ru-VE-szI" secondAttribute="bottom" id="asd-Fg-84K"/>
|
<constraint firstItem="9R5-6n-B3H" firstAttribute="trailing" secondItem="ytQ-Th-luv" secondAttribute="trailing" id="UuJ-8s-LEB"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="3Ru-VE-szI" secondAttribute="bottom" id="asd-Fg-84K"/>
|
||||||
<constraint firstItem="3Ru-VE-szI" firstAttribute="leading" secondItem="mYm-sh-pSj" secondAttribute="leading" id="bOP-2E-BEk"/>
|
<constraint firstItem="3Ru-VE-szI" firstAttribute="leading" secondItem="mYm-sh-pSj" secondAttribute="leading" id="bOP-2E-BEk"/>
|
||||||
<constraint firstItem="3Ru-VE-szI" firstAttribute="top" secondItem="mYm-sh-pSj" secondAttribute="top" id="cmd-fX-3Qa"/>
|
<constraint firstItem="3Ru-VE-szI" firstAttribute="top" secondItem="mYm-sh-pSj" secondAttribute="top" id="cmd-fX-3Qa"/>
|
||||||
|
<constraint firstItem="ytQ-Th-luv" firstAttribute="top" secondItem="9R5-6n-B3H" secondAttribute="top" id="mmu-vo-0BG"/>
|
||||||
|
<constraint firstItem="ytQ-Th-luv" firstAttribute="leading" secondItem="9R5-6n-B3H" secondAttribute="leading" id="pDL-PN-vlh"/>
|
||||||
|
<constraint firstItem="9R5-6n-B3H" firstAttribute="bottom" secondItem="ytQ-Th-luv" secondAttribute="bottom" id="pVh-Q9-Gqi"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="3Ru-VE-szI" secondAttribute="trailing" id="w0g-Ng-mkW"/>
|
<constraint firstAttribute="trailing" secondItem="3Ru-VE-szI" secondAttribute="trailing" id="w0g-Ng-mkW"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<viewLayoutGuide key="safeArea" id="9R5-6n-B3H"/>
|
<viewLayoutGuide key="safeArea" id="9R5-6n-B3H"/>
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="map" destination="3Ru-VE-szI" id="rYQ-5P-Kxr"/>
|
<outlet property="map" destination="3Ru-VE-szI" id="rYQ-5P-Kxr"/>
|
||||||
|
<outlet property="tableView" destination="ytQ-Th-luv" id="7Mf-aR-TGJ"/>
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="heB-JE-Wi6" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="heB-JE-Wi6" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="1881" y="1660"/>
|
<point key="canvasLocation" x="1881" y="1660"/>
|
||||||
</scene>
|
</scene>
|
||||||
|
<!--Location Edit Controller-->
|
||||||
|
<scene sceneID="Bjx-jy-Fp1">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="LocationEditController" id="HJt-oG-6ic" customClass="LocationEditController" customModule="AutoCat" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="DJI-63-46l">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<viewLayoutGuide key="safeArea" id="yiT-Qb-YfV"/>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="xxt-kh-jhI" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="2557.5999999999999" y="1658.3208395802101"/>
|
||||||
|
</scene>
|
||||||
<!--Search Controller-->
|
<!--Search Controller-->
|
||||||
<scene sceneID="3Md-yW-a0R">
|
<scene sceneID="3Md-yW-a0R">
|
||||||
<objects>
|
<objects>
|
||||||
@ -261,14 +328,14 @@
|
|||||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="VehicleCell" id="VEP-QD-i6y" customClass="VehicleCell" customModule="AutoCat" customModuleProvider="target">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="VehicleCell" id="VEP-QD-i6y" customClass="VehicleCell" customModule="AutoCat" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="28" width="375" height="85.5"/>
|
<rect key="frame" x="0.0" y="28" width="375" height="85"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VEP-QD-i6y" id="8hH-8I-XLB">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VEP-QD-i6y" id="8hH-8I-XLB">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="85.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="85"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Kia (JF) Optima" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AQY-7N-q8D">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Kia (JF) Optima" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AQY-7N-q8D">
|
||||||
<rect key="frame" x="8" y="8" width="124" height="21.5"/>
|
<rect key="frame" x="8" y="8" width="124" height="21"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
@ -280,7 +347,7 @@
|
|||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cvf-vM-QnT" customClass="PlateView" customModule="AutoCat" customModuleProvider="target">
|
<view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cvf-vM-QnT" customClass="PlateView" customModule="AutoCat" customModuleProvider="target">
|
||||||
<rect key="frame" x="8" y="37.5" width="317" height="40"/>
|
<rect key="frame" x="8" y="37" width="317" height="40"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="40" id="Xoz-Iw-PCU"/>
|
<constraint firstAttribute="height" constant="40" id="Xoz-Iw-PCU"/>
|
||||||
|
|||||||
26
AutoCat/Cells/EventCell.swift
Normal file
26
AutoCat/Cells/EventCell.swift
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import UIKit
|
||||||
|
|
||||||
|
class EventCell: UITableViewCell {
|
||||||
|
@IBOutlet weak var address: UILabel!
|
||||||
|
@IBOutlet weak var date: UILabel!
|
||||||
|
|
||||||
|
let dateFormatter = DateFormatter()
|
||||||
|
|
||||||
|
override func awakeFromNib() {
|
||||||
|
super.awakeFromNib()
|
||||||
|
|
||||||
|
self.dateFormatter.dateStyle = .short
|
||||||
|
self.dateFormatter.timeStyle = .short
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(with event: VehicleEvent) {
|
||||||
|
if let addressString = event.address {
|
||||||
|
self.address.text = addressString
|
||||||
|
} else {
|
||||||
|
self.address.text = "Lat: \(event.latitude), Lon: \(event.longitude)"
|
||||||
|
}
|
||||||
|
|
||||||
|
let date = Date(timeIntervalSince1970: event.date)
|
||||||
|
self.date.text = self.dateFormatter.string(from: date)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,68 +0,0 @@
|
|||||||
import UIKit
|
|
||||||
import MapKit
|
|
||||||
|
|
||||||
class EventPin: NSObject, MKAnnotation {
|
|
||||||
var coordinate: CLLocationCoordinate2D
|
|
||||||
var title: String?
|
|
||||||
var subtitle: String?
|
|
||||||
|
|
||||||
init(coordinate: CLLocationCoordinate2D, title: String?, subtitle: String) {
|
|
||||||
self.coordinate = coordinate
|
|
||||||
self.title = title
|
|
||||||
self.subtitle = subtitle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EventsController: UIViewController {
|
|
||||||
|
|
||||||
@IBOutlet weak var map: MKMapView!
|
|
||||||
|
|
||||||
public var events: [VehicleEvent] = [] {
|
|
||||||
didSet {
|
|
||||||
self.pins = self.events.map { event in
|
|
||||||
let coordinate = CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude)
|
|
||||||
let subtitle = event.address ?? "\(event.latitude), \(event.longitude)"
|
|
||||||
let date = Date(timeIntervalSince1970: event.date)
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.dateStyle = .medium
|
|
||||||
formatter.timeStyle = .short
|
|
||||||
let title = formatter.string(from: date)
|
|
||||||
return EventPin(coordinate: coordinate, title: title, subtitle: subtitle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var pins: [EventPin] = []
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
#if targetEnvironment(macCatalyst)
|
|
||||||
self.map.showsZoomControls = true
|
|
||||||
#endif
|
|
||||||
|
|
||||||
self.map.addAnnotations(self.pins)
|
|
||||||
self.centerMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
func centerMap() {
|
|
||||||
var minLat = 0.0, maxLat = 0.0, minLon = 0.0, maxLon = 0.0
|
|
||||||
for event in self.events {
|
|
||||||
if event.latitude < minLat || minLat == 0 { minLat = event.latitude }
|
|
||||||
if event.latitude > maxLat || maxLat == 0 { maxLat = event.latitude }
|
|
||||||
if event.longitude < minLon || minLon == 0 { minLon = event.longitude }
|
|
||||||
if event.longitude > maxLon || maxLon == 0 { maxLon = event.longitude }
|
|
||||||
}
|
|
||||||
|
|
||||||
let center = CLLocationCoordinate2D(latitude: (minLat + maxLat)/2, longitude: (minLon + maxLon)/2)
|
|
||||||
let leftTop = CLLocation(latitude: minLat, longitude: minLon)
|
|
||||||
let rightBottom = CLLocation(latitude: maxLat, longitude: maxLon)
|
|
||||||
var diagonal = leftTop.distance(from: rightBottom)
|
|
||||||
if diagonal < 1000 {
|
|
||||||
diagonal = 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
let region = MKCoordinateRegion(center: center, latitudinalMeters: diagonal, longitudinalMeters: diagonal)
|
|
||||||
self.map.setRegion(region, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
251
AutoCat/Controllers/Location/EventsController.swift
Normal file
251
AutoCat/Controllers/Location/EventsController.swift
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
import UIKit
|
||||||
|
import MapKit
|
||||||
|
import RxSwift
|
||||||
|
import Realm
|
||||||
|
import RealmSwift
|
||||||
|
|
||||||
|
class EventPin: NSObject, MKAnnotation {
|
||||||
|
var coordinate: CLLocationCoordinate2D
|
||||||
|
var title: String?
|
||||||
|
var subtitle: String?
|
||||||
|
|
||||||
|
init(coordinate: CLLocationCoordinate2D, title: String?, subtitle: String) {
|
||||||
|
self.coordinate = coordinate
|
||||||
|
self.title = title
|
||||||
|
self.subtitle = subtitle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EventsMode {
|
||||||
|
case map
|
||||||
|
case list
|
||||||
|
}
|
||||||
|
|
||||||
|
class EventsController: UIViewController, UITableViewDataSource, UITableViewDelegate {
|
||||||
|
|
||||||
|
@IBOutlet weak var map: MKMapView!
|
||||||
|
@IBOutlet weak var tableView: UITableView!
|
||||||
|
|
||||||
|
let bag = DisposeBag()
|
||||||
|
var modeButton: UIBarButtonItem!
|
||||||
|
var addButton: UIBarButtonItem!
|
||||||
|
var mode: EventsMode = .map
|
||||||
|
|
||||||
|
public var vehicle: Vehicle? {
|
||||||
|
didSet {
|
||||||
|
if let vehicle = self.vehicle {
|
||||||
|
self.pins = vehicle.events.map { event in
|
||||||
|
let coordinate = CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude)
|
||||||
|
let subtitle = event.address ?? "\(event.latitude), \(event.longitude)"
|
||||||
|
let date = Date(timeIntervalSince1970: event.date)
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateStyle = .medium
|
||||||
|
formatter.timeStyle = .short
|
||||||
|
let title = formatter.string(from: date)
|
||||||
|
return EventPin(coordinate: coordinate, title: title, subtitle: subtitle)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.isViewLoaded {
|
||||||
|
self.updateInterface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var pins: [EventPin] = []
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
self.title = self.vehicle?.number ?? "Events"
|
||||||
|
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
self.map.showsZoomControls = true
|
||||||
|
#endif
|
||||||
|
|
||||||
|
self.modeButton = UIBarButtonItem(image: UIImage(systemName: "list.bullet"), style: .plain, target: self, action: #selector(switchMode(_:)))
|
||||||
|
self.addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addEvent(_:)))
|
||||||
|
self.navigationItem.rightBarButtonItems = [self.modeButton, self.addButton]
|
||||||
|
|
||||||
|
self.updateInterface()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateInterface() {
|
||||||
|
self.map.removeAnnotations(self.map.annotations)
|
||||||
|
self.map.addAnnotations(self.pins)
|
||||||
|
self.centerMap()
|
||||||
|
self.tableView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
func centerMap() {
|
||||||
|
guard let vehicle = self.vehicle else { return }
|
||||||
|
|
||||||
|
var minLat = 0.0, maxLat = 0.0, minLon = 0.0, maxLon = 0.0
|
||||||
|
for event in vehicle.events {
|
||||||
|
if event.latitude < minLat || minLat == 0 { minLat = event.latitude }
|
||||||
|
if event.latitude > maxLat || maxLat == 0 { maxLat = event.latitude }
|
||||||
|
if event.longitude < minLon || minLon == 0 { minLon = event.longitude }
|
||||||
|
if event.longitude > maxLon || maxLon == 0 { maxLon = event.longitude }
|
||||||
|
}
|
||||||
|
|
||||||
|
let center = CLLocationCoordinate2D(latitude: (minLat + maxLat)/2, longitude: (minLon + maxLon)/2)
|
||||||
|
let leftTop = CLLocation(latitude: minLat, longitude: minLon)
|
||||||
|
let rightBottom = CLLocation(latitude: maxLat, longitude: maxLon)
|
||||||
|
var diagonal = leftTop.distance(from: rightBottom)*1.2
|
||||||
|
if diagonal < 1000 {
|
||||||
|
diagonal = 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
let region = MKCoordinateRegion(center: center, latitudinalMeters: diagonal, longitudinalMeters: diagonal)
|
||||||
|
self.map.setRegion(region, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func switchMode(_ sender: UIBarButtonItem) {
|
||||||
|
switch self.mode {
|
||||||
|
case .map:
|
||||||
|
self.mode = .list
|
||||||
|
self.tableView.reloadData()
|
||||||
|
self.tableView.isHidden = false
|
||||||
|
self.map.isHidden = true
|
||||||
|
self.modeButton.image = UIImage(systemName: "map")
|
||||||
|
case .list:
|
||||||
|
self.mode = .map
|
||||||
|
self.tableView.isHidden = true
|
||||||
|
self.map.isHidden = false
|
||||||
|
self.modeButton.image = UIImage(systemName: "list.bullet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - UITableViewDataSource
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
|
return self.vehicle?.events.count ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
guard let cell = tableView.dequeueReusableCell(withIdentifier: "EventCell", for: indexPath) as? EventCell else {
|
||||||
|
return UITableViewCell()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let event = self.vehicle?.events[indexPath.row] {
|
||||||
|
cell.configure(with: event)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - UITableViewDelegate
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
||||||
|
let delete = UIContextualAction(style: .destructive, title: "Delete") { action, view, completion in
|
||||||
|
self.deleteEvent(index: indexPath.row, completion: completion)
|
||||||
|
}
|
||||||
|
delete.image = UIImage(systemName: "trash")
|
||||||
|
|
||||||
|
let edit = UIContextualAction(style: .normal, title: "Edit") { action, view, completion in
|
||||||
|
self.editEvent(index: indexPath.row)
|
||||||
|
completion(true)
|
||||||
|
}
|
||||||
|
edit.image = UIImage(systemName: "pencil")
|
||||||
|
edit.backgroundColor = .systemBlue
|
||||||
|
|
||||||
|
let configuration = UISwipeActionsConfiguration(actions: [delete, edit])
|
||||||
|
configuration.performsFirstActionWithFullSwipe = false
|
||||||
|
return configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Event actions
|
||||||
|
|
||||||
|
func deleteEvent(index: Int, completion: @escaping (Bool) -> Void) {
|
||||||
|
guard let vehicle = self.vehicle else {
|
||||||
|
IHProgressHUD.showError(withStatus: "Unknown vehicle")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let event = vehicle.events[index]
|
||||||
|
if let eventId = event.id {
|
||||||
|
IHProgressHUD.show()
|
||||||
|
Api.remove(event: eventId).observeOn(MainScheduler.instance).subscribe(onSuccess: { vehicle in
|
||||||
|
completion(self.update(vehicle: vehicle))
|
||||||
|
}, onError: { error in
|
||||||
|
completion(false)
|
||||||
|
IHProgressHUD.show(error: error)
|
||||||
|
print(error)
|
||||||
|
}).disposed(by: self.bag)
|
||||||
|
} else {
|
||||||
|
self.showAlert(title: "Error", message: "Event ID is not found. Please try to update vehicle record, containing this event.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func editEvent(index: Int) {
|
||||||
|
guard let vehicle = self.vehicle else {
|
||||||
|
IHProgressHUD.showError(withStatus: "Unknown vehicle")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let event = vehicle.events[index]
|
||||||
|
let sb = UIStoryboard(name: "Main", bundle: nil)
|
||||||
|
let controller = sb.instantiateViewController(identifier: "LocationEditController") as LocationEditController
|
||||||
|
controller.title = "Edit event"
|
||||||
|
controller.date = Date(timeIntervalSince1970: event.date)
|
||||||
|
controller.placemark = Placemark(latitude: event.latitude, longitude: event.longitude, address: event.address)
|
||||||
|
controller.onDone = { newEvent in
|
||||||
|
newEvent.id = event.id
|
||||||
|
self.navigationController?.popViewController(animated: true, completion: {
|
||||||
|
IHProgressHUD.show()
|
||||||
|
Api.edit(event: newEvent)
|
||||||
|
.observeOn(MainScheduler.instance)
|
||||||
|
.subscribe(onSuccess: { self.update(vehicle: $0) }, onError:
|
||||||
|
{ error in
|
||||||
|
IHProgressHUD.show(error: error)
|
||||||
|
})
|
||||||
|
.disposed(by: self.bag)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
self.navigationController?.pushViewController(controller, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func addEvent(_ sender: UIBarButtonItem) {
|
||||||
|
guard let vehicle = self.vehicle else {
|
||||||
|
IHProgressHUD.showError(withStatus: "Unknown vehicle")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let sb = UIStoryboard(name: "Main", bundle: nil)
|
||||||
|
let controller = sb.instantiateViewController(identifier: "LocationEditController") as LocationEditController
|
||||||
|
controller.title = "Add new event"
|
||||||
|
controller.onDone = { newEvent in
|
||||||
|
self.navigationController?.popViewController(animated: true, completion: {
|
||||||
|
IHProgressHUD.show()
|
||||||
|
Api.add(event: newEvent, to: vehicle.number)
|
||||||
|
.observeOn(MainScheduler.instance)
|
||||||
|
.subscribe(onSuccess: { self.update(vehicle: $0) }, onError:
|
||||||
|
{ error in
|
||||||
|
IHProgressHUD.show(error: error)
|
||||||
|
})
|
||||||
|
.disposed(by: self.bag)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
self.navigationController?.pushViewController(controller, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func update(vehicle: Vehicle) -> Bool {
|
||||||
|
do {
|
||||||
|
if let realm = self.vehicle?.realm {
|
||||||
|
try realm.write {
|
||||||
|
realm.add(vehicle, update: .all)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.vehicle = vehicle
|
||||||
|
IHProgressHUD.dismiss()
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
IHProgressHUD.show(error: error)
|
||||||
|
print(error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
AutoCat/Controllers/Location/LocationEditController.swift
Normal file
55
AutoCat/Controllers/Location/LocationEditController.swift
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import UIKit
|
||||||
|
import Eureka
|
||||||
|
import RxSwift
|
||||||
|
import CoreLocation
|
||||||
|
|
||||||
|
class LocationEditController: FormViewController {
|
||||||
|
|
||||||
|
private let bag = DisposeBag()
|
||||||
|
private var doneButton: UIBarButtonItem!
|
||||||
|
|
||||||
|
var date = Date()
|
||||||
|
var placemark: Placemark? = nil
|
||||||
|
|
||||||
|
var onDone: ((VehicleEvent) -> Void)?
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
self.doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTapped(_:)))
|
||||||
|
self.navigationItem.rightBarButtonItem = self.doneButton
|
||||||
|
self.doneButton.isEnabled = false
|
||||||
|
|
||||||
|
form +++ Section("")
|
||||||
|
<<< DateTimeInlineRow(){
|
||||||
|
$0.title = "Date and time"
|
||||||
|
$0.value = self.date
|
||||||
|
}.onChange { row in
|
||||||
|
if let newDate = row.value {
|
||||||
|
self.date = newDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<<< LocationRow() { row in
|
||||||
|
row.title = "Location"
|
||||||
|
row.value = self.placemark
|
||||||
|
}.onChange { row in
|
||||||
|
if let newPlacemark = row.value {
|
||||||
|
self.placemark = newPlacemark
|
||||||
|
self.doneButton.isEnabled = true
|
||||||
|
} else {
|
||||||
|
self.doneButton.isEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func doneTapped(_ sender: UIBarButtonItem) {
|
||||||
|
guard let placemark = self.placemark else { return }
|
||||||
|
let event = VehicleEvent(lat: placemark.latitude, lon: placemark.longitude, speed: -1, dir: -1)
|
||||||
|
event.date = self.date.timeIntervalSince1970
|
||||||
|
if let address = placemark.address {
|
||||||
|
event.address = address
|
||||||
|
}
|
||||||
|
self.onDone?(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
156
AutoCat/Controllers/Location/LocationPickerController.swift
Normal file
156
AutoCat/Controllers/Location/LocationPickerController.swift
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
import Foundation
|
||||||
|
import MapKit
|
||||||
|
import Eureka
|
||||||
|
import RxSwift
|
||||||
|
import Intents
|
||||||
|
|
||||||
|
public struct Placemark: Equatable {
|
||||||
|
var latitude: Double
|
||||||
|
var longitude: Double
|
||||||
|
var address: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LocationPickerController : UIViewController, TypedRowControllerType, MKMapViewDelegate {
|
||||||
|
|
||||||
|
public var row: RowOf<Placemark>!
|
||||||
|
public var onDismissCallback: ((UIViewController) -> ())?
|
||||||
|
|
||||||
|
private let bag = DisposeBag()
|
||||||
|
private var geocodingDisposable: Disposable?
|
||||||
|
private var address: String?
|
||||||
|
|
||||||
|
lazy var mapView : MKMapView = { [unowned self] in
|
||||||
|
let v = MKMapView(frame: self.view.bounds)
|
||||||
|
v.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
return v
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var pinView: UIImageView = { [unowned self] in
|
||||||
|
let v = UIImageView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
|
||||||
|
v.image = UIImage(named: "MapPin", in: Bundle(for: LocationPickerController.self), compatibleWith: nil)
|
||||||
|
v.image = v.image?.withRenderingMode(.alwaysTemplate)
|
||||||
|
v.tintColor = self.view.tintColor
|
||||||
|
v.backgroundColor = .clear
|
||||||
|
v.clipsToBounds = true
|
||||||
|
v.contentMode = .scaleAspectFit
|
||||||
|
v.isUserInteractionEnabled = false
|
||||||
|
return v
|
||||||
|
}()
|
||||||
|
|
||||||
|
let width: CGFloat = 10.0
|
||||||
|
let height: CGFloat = 5.0
|
||||||
|
|
||||||
|
lazy var ellipse: UIBezierPath = { [unowned self] in
|
||||||
|
let ellipse = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: self.width, height: self.height))
|
||||||
|
return ellipse
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
lazy var ellipsisLayer: CAShapeLayer = { [unowned self] in
|
||||||
|
let layer = CAShapeLayer()
|
||||||
|
layer.bounds = CGRect(x: 0, y: 0, width: self.width, height: self.height)
|
||||||
|
layer.path = self.ellipse.cgPath
|
||||||
|
layer.fillColor = UIColor.gray.cgColor
|
||||||
|
layer.fillRule = .nonZero
|
||||||
|
layer.lineCap = .butt
|
||||||
|
layer.lineDashPattern = nil
|
||||||
|
layer.lineDashPhase = 0.0
|
||||||
|
layer.lineJoin = .miter
|
||||||
|
layer.lineWidth = 1.0
|
||||||
|
layer.miterLimit = 10.0
|
||||||
|
layer.strokeColor = UIColor.gray.cgColor
|
||||||
|
return layer
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
required public init?(coder aDecoder: NSCoder) {
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
||||||
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
convenience public init(_ callback: ((UIViewController) -> ())?){
|
||||||
|
self.init(nibName: nil, bundle: nil)
|
||||||
|
onDismissCallback = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
view.addSubview(mapView)
|
||||||
|
|
||||||
|
mapView.delegate = self
|
||||||
|
mapView.addSubview(pinView)
|
||||||
|
mapView.layer.insertSublayer(ellipsisLayer, below: pinView.layer)
|
||||||
|
|
||||||
|
let button = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(LocationPickerController.tappedDone(_:)))
|
||||||
|
button.title = "Done"
|
||||||
|
navigationItem.rightBarButtonItem = button
|
||||||
|
|
||||||
|
if let value = row.value {
|
||||||
|
let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: value.latitude, longitude: value.longitude), latitudinalMeters: 1000, longitudinalMeters: 1000)
|
||||||
|
mapView.setRegion(region, animated: true)
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
mapView.showsUserLocation = true
|
||||||
|
}
|
||||||
|
updateTitle()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
let center = mapView.convert(mapView.centerCoordinate, toPointTo: pinView)
|
||||||
|
pinView.center = CGPoint(x: center.x, y: center.y - (pinView.bounds.height/2))
|
||||||
|
ellipsisLayer.position = center
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@objc func tappedDone(_ sender: UIBarButtonItem){
|
||||||
|
let target = mapView.convert(ellipsisLayer.position, toCoordinateFrom: mapView)
|
||||||
|
row.value = Placemark(latitude: target.latitude, longitude: target.longitude, address: self.address)
|
||||||
|
onDismissCallback?(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateTitle(){
|
||||||
|
let fmt = NumberFormatter()
|
||||||
|
fmt.maximumFractionDigits = 4
|
||||||
|
fmt.minimumFractionDigits = 4
|
||||||
|
let latitude = fmt.string(from: NSNumber(value: mapView.centerCoordinate.latitude))!
|
||||||
|
let longitude = fmt.string(from: NSNumber(value: mapView.centerCoordinate.longitude))!
|
||||||
|
title = "\(latitude), \(longitude)"
|
||||||
|
|
||||||
|
self.address = nil
|
||||||
|
self.geocodingDisposable?.dispose()
|
||||||
|
self.geocodingDisposable = LocationManager
|
||||||
|
.getAddressForLocation(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude)
|
||||||
|
.observeOn(MainScheduler.instance)
|
||||||
|
.subscribe(onSuccess: { address in
|
||||||
|
self.title = address
|
||||||
|
self.address = address
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
|
||||||
|
ellipsisLayer.transform = CATransform3DMakeScale(0.5, 0.5, 1)
|
||||||
|
UIView.animate(withDuration: 0.2, animations: { [weak self] in
|
||||||
|
self?.pinView.center = CGPoint(x: self!.pinView.center.x, y: self!.pinView.center.y - 10)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
|
||||||
|
ellipsisLayer.transform = CATransform3DIdentity
|
||||||
|
UIView.animate(withDuration: 0.2, animations: { [weak self] in
|
||||||
|
self?.pinView.center = CGPoint(x: self!.pinView.center.x, y: self!.pinView.center.y + 10)
|
||||||
|
})
|
||||||
|
updateTitle()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
|
||||||
|
mapView.showsUserLocation = false
|
||||||
|
let region = MKCoordinateRegion(center: userLocation.coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
|
||||||
|
mapView.setRegion(region, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
57
AutoCat/Controllers/Location/LocationRow.swift
Normal file
57
AutoCat/Controllers/Location/LocationRow.swift
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import UIKit
|
||||||
|
import Eureka
|
||||||
|
import CoreLocation
|
||||||
|
|
||||||
|
public final class LocationRow: OptionsRow<PushSelectorCell<Placemark>>, PresenterRowType, RowType {
|
||||||
|
|
||||||
|
public typealias PresenterRow = LocationPickerController
|
||||||
|
|
||||||
|
/// Defines how the view controller will be presented, pushed, etc.
|
||||||
|
public var presentationMode: PresentationMode<PresenterRow>?
|
||||||
|
|
||||||
|
/// Will be called before the presentation occurs.
|
||||||
|
public var onPresentCallback: ((FormViewController, PresenterRow) -> Void)?
|
||||||
|
|
||||||
|
public required init(tag: String?) {
|
||||||
|
super.init(tag: tag)
|
||||||
|
presentationMode = .show(controllerProvider: ControllerProvider.callback { return LocationPickerController(){ _ in } }, onDismiss: { vc in _ = vc.navigationController?.popViewController(animated: true) })
|
||||||
|
|
||||||
|
displayValueFor = {
|
||||||
|
guard let placemark = $0 else { return "" }
|
||||||
|
let fmt = NumberFormatter()
|
||||||
|
fmt.maximumFractionDigits = 4
|
||||||
|
fmt.minimumFractionDigits = 4
|
||||||
|
let latitude = fmt.string(from: NSNumber(value: placemark.latitude))!
|
||||||
|
let longitude = fmt.string(from: NSNumber(value: placemark.longitude))!
|
||||||
|
return "\(latitude), \(longitude)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Extends `didSelect` method
|
||||||
|
*/
|
||||||
|
public override func customDidSelect() {
|
||||||
|
super.customDidSelect()
|
||||||
|
guard let presentationMode = presentationMode, !isDisabled else { return }
|
||||||
|
if let controller = presentationMode.makeController() {
|
||||||
|
controller.row = self
|
||||||
|
controller.title = selectorTitle ?? controller.title
|
||||||
|
onPresentCallback?(cell.formViewController()!, controller)
|
||||||
|
presentationMode.present(controller, row: self, presentingController: self.cell.formViewController()!)
|
||||||
|
} else {
|
||||||
|
presentationMode.present(nil, row: self, presentingController: self.cell.formViewController()!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Prepares the pushed row setting its title and completion callback.
|
||||||
|
*/
|
||||||
|
public override func prepare(for segue: UIStoryboardSegue) {
|
||||||
|
super.prepare(for: segue)
|
||||||
|
guard let rowVC = segue.destination as? PresenterRow else { return }
|
||||||
|
rowVC.title = selectorTitle ?? rowVC.title
|
||||||
|
rowVC.onDismissCallback = presentationMode?.onDismissCallback ?? rowVC.onDismissCallback
|
||||||
|
onPresentCallback?(cell.formViewController()!, rowVC)
|
||||||
|
rowVC.row = self
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -159,7 +159,6 @@ class RecordsController: UIViewController, UITableViewDelegate {
|
|||||||
return AudioRecord(path: url.lastPathComponent, number: self.getPlateNumber(from: text), raw: text, duration: duration, event: event)
|
return AudioRecord(path: url.lastPathComponent, number: self.getPlateNumber(from: text), raw: text, duration: duration, event: event)
|
||||||
}
|
}
|
||||||
.subscribe(onSuccess: { record in
|
.subscribe(onSuccess: { record in
|
||||||
print(record)
|
|
||||||
let realm = try? Realm()
|
let realm = try? Realm()
|
||||||
try? realm?.write {
|
try? realm?.write {
|
||||||
realm?.add(record)
|
realm?.add(record)
|
||||||
|
|||||||
@ -274,8 +274,7 @@ class ReportController: UIViewController, UICollectionViewDataSource, UICollecti
|
|||||||
}
|
}
|
||||||
else if let events = self.vehicle?.events, indexPath.row == ReportGeneralSection.events.rawValue && events.count > 0 {
|
else if let events = self.vehicle?.events, indexPath.row == ReportGeneralSection.events.rawValue && events.count > 0 {
|
||||||
let controller = sb.instantiateViewController(identifier: "EventsController") as EventsController
|
let controller = sb.instantiateViewController(identifier: "EventsController") as EventsController
|
||||||
controller.events = Array(events)
|
controller.vehicle = self.vehicle
|
||||||
controller.title = self.vehicle?.number ?? "Events"
|
|
||||||
self.navigationController?.pushViewController(controller, animated: true)
|
self.navigationController?.pushViewController(controller, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
AutoCat/Extensions/Navigation.swift
Normal file
32
AutoCat/Extensions/Navigation.swift
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UINavigationController {
|
||||||
|
public func pushViewController(
|
||||||
|
_ viewController: UIViewController,
|
||||||
|
animated: Bool,
|
||||||
|
completion: @escaping () -> Void)
|
||||||
|
{
|
||||||
|
pushViewController(viewController, animated: animated)
|
||||||
|
|
||||||
|
guard animated, let coordinator = transitionCoordinator else {
|
||||||
|
DispatchQueue.main.async { completion() }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
coordinator.animate(alongsideTransition: nil) { _ in completion() }
|
||||||
|
}
|
||||||
|
|
||||||
|
func popViewController(
|
||||||
|
animated: Bool,
|
||||||
|
completion: @escaping () -> Void)
|
||||||
|
{
|
||||||
|
popViewController(animated: animated)
|
||||||
|
|
||||||
|
guard animated, let coordinator = transitionCoordinator else {
|
||||||
|
DispatchQueue.main.async { completion() }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
coordinator.animate(alongsideTransition: nil) { _ in completion() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +1,10 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import RealmSwift
|
import RealmSwift
|
||||||
import RxSwift
|
import RxSwift
|
||||||
|
import CoreLocation
|
||||||
|
|
||||||
class VehicleEvent: Object, Codable {
|
public class VehicleEvent: Object, Codable {
|
||||||
|
@objc dynamic var id: String?
|
||||||
@objc dynamic var date: TimeInterval = Date().timeIntervalSince1970
|
@objc dynamic var date: TimeInterval = Date().timeIntervalSince1970
|
||||||
@objc dynamic var latitude: Double = 0
|
@objc dynamic var latitude: Double = 0
|
||||||
@objc dynamic var longitude: Double = 0
|
@objc dynamic var longitude: Double = 0
|
||||||
@ -10,6 +12,10 @@ class VehicleEvent: Object, Codable {
|
|||||||
@objc dynamic var direction: Double = 0
|
@objc dynamic var direction: Double = 0
|
||||||
@objc dynamic var address: String? = nil
|
@objc dynamic var address: String? = nil
|
||||||
|
|
||||||
|
var coordinate: CLLocationCoordinate2D {
|
||||||
|
return CLLocationCoordinate2D(latitude: self.latitude, longitude: self.longitude)
|
||||||
|
}
|
||||||
|
|
||||||
init(lat: Double, lon: Double, speed: Double, dir: Double) {
|
init(lat: Double, lon: Double, speed: Double, dir: Double) {
|
||||||
self.latitude = lat
|
self.latitude = lat
|
||||||
self.longitude = lon
|
self.longitude = lon
|
||||||
|
|||||||
@ -40,14 +40,14 @@ class Api {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return URLSession.shared.rx.data(request: request).asSingle().map { data in
|
return URLSession.shared.rx.data(request: request).asSingle().map { data in
|
||||||
let str = String(data: data, encoding: .utf8)
|
// let str = String(data: data, encoding: .utf8)
|
||||||
print("================================")
|
// print("================================")
|
||||||
if let string = str?.replacingOccurrences(of: "\\\"", with: "\"")
|
// if let string = str?.replacingOccurrences(of: "\\\"", with: "\"")
|
||||||
.replacingOccurrences(of: "\\'", with: "'")
|
// .replacingOccurrences(of: "\\'", with: "'")
|
||||||
.replacingOccurrences(of: "\\n", with: "") {
|
// .replacingOccurrences(of: "\\n", with: "") {
|
||||||
print(string)
|
// print(string)
|
||||||
}
|
// }
|
||||||
print("================================")
|
// print("================================")
|
||||||
let resp = try JSONDecoder().decode(Response<T>.self, from: data)
|
let resp = try JSONDecoder().decode(Response<T>.self, from: data)
|
||||||
if resp.success {
|
if resp.success {
|
||||||
return resp.data!
|
return resp.data!
|
||||||
@ -231,4 +231,14 @@ class Api {
|
|||||||
return vehicle
|
return vehicle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func remove(event id: String) -> Single<Vehicle> {
|
||||||
|
let body = ["eventId": id]
|
||||||
|
return self.makeBodyRequest(api: "events", body: body, method: "DELETE")
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func edit(event: VehicleEvent) -> Single<Vehicle> {
|
||||||
|
let body = ["event": event]
|
||||||
|
return self.makeBodyRequest(api: "events", body: body, method: "PUT")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,12 +106,7 @@ class LocationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func requestCurrentLocation() -> Single<VehicleEvent> {
|
static func requestCurrentLocation() -> Single<VehicleEvent> {
|
||||||
return self.checkPermissions().flatMap(self.requestLocation).do(onSuccess: { event in
|
return self.checkPermissions().flatMap(self.requestLocation).do(onDispose: {
|
||||||
print("Get location success")
|
|
||||||
}, onSubscribed: {
|
|
||||||
print("Get location subscribed")
|
|
||||||
}, onDispose: {
|
|
||||||
print("Get location dispose")
|
|
||||||
self.manager.stopUpdatingLocation()
|
self.manager.stopUpdatingLocation()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -105,7 +105,7 @@ class Recorder {
|
|||||||
if let transcription = result?.bestTranscription {
|
if let transcription = result?.bestTranscription {
|
||||||
self.result = transcription.formattedString
|
self.result = transcription.formattedString
|
||||||
self.endRecognitionTimer?.invalidate()
|
self.endRecognitionTimer?.invalidate()
|
||||||
self.endRecognitionTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { timer in
|
self.endRecognitionTimer = Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { timer in
|
||||||
self.finishRecording()
|
self.finishRecording()
|
||||||
observer(.success(self.result))
|
observer(.success(self.result))
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user