SwiftUI version of map screens

This commit is contained in:
Selim Mustafaev 2025-04-13 22:00:21 +03:00
parent 59ad858234
commit 831cf02127
11 changed files with 274 additions and 203 deletions

View File

@ -46,6 +46,12 @@
7A2E11292CCE395300E5CA17 /* OptionalDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2E11282CCE395300E5CA17 /* OptionalDatePicker.swift */; }; 7A2E11292CCE395300E5CA17 /* OptionalDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2E11282CCE395300E5CA17 /* OptionalDatePicker.swift */; };
7A2E6FA72C42B3AD00C40DA7 /* AutoCatCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; }; 7A2E6FA72C42B3AD00C40DA7 /* AutoCatCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; };
7A3399AB299063370087DF98 /* SearchControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3399AA299063370087DF98 /* SearchControllerExt.swift */; }; 7A3399AB299063370087DF98 /* SearchControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3399AA299063370087DF98 /* SearchControllerExt.swift */; };
7A386A402DABDC190051676A /* MapScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A3F2DABDC190051676A /* MapScreen.swift */; };
7A386A442DABDC360051676A /* MapViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A432DABDC360051676A /* MapViewModel.swift */; };
7A386A462DABDC660051676A /* MapCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A452DABDC660051676A /* MapCoordinator.swift */; };
7A386A482DABE0D00051676A /* MapMarkerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A472DABE0D00051676A /* MapMarkerModel.swift */; };
7A386A4B2DAC35F10051676A /* ClusterMap in Frameworks */ = {isa = PBXBuildFile; productRef = 7A386A4A2DAC35F10051676A /* ClusterMap */; };
7A386A4D2DAC35F10051676A /* ClusterMapSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 7A386A4C2DAC35F10051676A /* ClusterMapSwiftUI */; };
7A3F07AB24360DC800E59687 /* Dated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3F07AA24360DC800E59687 /* Dated.swift */; }; 7A3F07AB24360DC800E59687 /* Dated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3F07AA24360DC800E59687 /* Dated.swift */; };
7A4322912CB2CC8A00085CF6 /* FiltersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322902CB2CC8A00085CF6 /* FiltersScreen.swift */; }; 7A4322912CB2CC8A00085CF6 /* FiltersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322902CB2CC8A00085CF6 /* FiltersScreen.swift */; };
7A4322932CB2CCAA00085CF6 /* FiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */; }; 7A4322932CB2CCAA00085CF6 /* FiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */; };
@ -185,10 +191,8 @@
7ACBB91E2CB9B155005A5168 /* Mockable in Frameworks */ = {isa = PBXBuildFile; productRef = 7ACBB91D2CB9B155005A5168 /* Mockable */; }; 7ACBB91E2CB9B155005A5168 /* Mockable in Frameworks */ = {isa = PBXBuildFile; productRef = 7ACBB91D2CB9B155005A5168 /* Mockable */; };
7ACBB9202CB9B16C005A5168 /* Mockable in Frameworks */ = {isa = PBXBuildFile; productRef = 7ACBB91F2CB9B16C005A5168 /* Mockable */; }; 7ACBB9202CB9B16C005A5168 /* Mockable in Frameworks */ = {isa = PBXBuildFile; productRef = 7ACBB91F2CB9B16C005A5168 /* Mockable */; };
7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C92250B954900F237B2 /* Navigation.swift */; }; 7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C92250B954900F237B2 /* Navigation.swift */; };
7ADF6C95250D037700F237B2 /* ShowEventController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C94250D037700F237B2 /* ShowEventController.swift */; };
7ADF6C97250F41B000F237B2 /* PNKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C96250F41B000F237B2 /* PNKeyboard.swift */; }; 7ADF6C97250F41B000F237B2 /* PNKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C96250F41B000F237B2 /* PNKeyboard.swift */; };
7ADF6C99250F872C00F237B2 /* RoadNumbers.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7ADF6C98250F872C00F237B2 /* RoadNumbers.otf */; }; 7ADF6C99250F872C00F237B2 /* RoadNumbers.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7ADF6C98250F872C00F237B2 /* RoadNumbers.otf */; };
7ADF6C9F251201D200F237B2 /* GlobalEventsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C9E251201D200F237B2 /* GlobalEventsController.swift */; };
7ADF6CA12512244400F237B2 /* MapExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6CA02512244400F237B2 /* MapExt.swift */; }; 7ADF6CA12512244400F237B2 /* MapExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6CA02512244400F237B2 /* MapExt.swift */; };
7AE24C5F251F1B4E00758E39 /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE24C5E251F1B4E00758E39 /* Buttons.swift */; }; 7AE24C5F251F1B4E00758E39 /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE24C5E251F1B4E00758E39 /* Buttons.swift */; };
7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */; }; 7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */; };
@ -325,6 +329,10 @@
7A2E6FA32C42B3AD00C40DA7 /* AutoCatCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AutoCatCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 7A2E6FA32C42B3AD00C40DA7 /* AutoCatCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AutoCatCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
7A333813249A532400D878F1 /* Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = "<group>"; }; 7A333813249A532400D878F1 /* Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = "<group>"; };
7A3399AA299063370087DF98 /* SearchControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchControllerExt.swift; sourceTree = "<group>"; }; 7A3399AA299063370087DF98 /* SearchControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchControllerExt.swift; sourceTree = "<group>"; };
7A386A3F2DABDC190051676A /* MapScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapScreen.swift; sourceTree = "<group>"; };
7A386A432DABDC360051676A /* MapViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewModel.swift; sourceTree = "<group>"; };
7A386A452DABDC660051676A /* MapCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapCoordinator.swift; sourceTree = "<group>"; };
7A386A472DABE0D00051676A /* MapMarkerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapMarkerModel.swift; sourceTree = "<group>"; };
7A3F07AA24360DC800E59687 /* Dated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dated.swift; sourceTree = "<group>"; }; 7A3F07AA24360DC800E59687 /* Dated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dated.swift; sourceTree = "<group>"; };
7A4322902CB2CC8A00085CF6 /* FiltersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersScreen.swift; sourceTree = "<group>"; }; 7A4322902CB2CC8A00085CF6 /* FiltersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersScreen.swift; sourceTree = "<group>"; };
7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewModel.swift; sourceTree = "<group>"; }; 7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewModel.swift; sourceTree = "<group>"; };
@ -467,10 +475,8 @@
7AC76D7A270083AE0084DB27 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = "<group>"; }; 7AC76D7A270083AE0084DB27 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = "<group>"; };
7AC8B2752D6A01C700190706 /* UISearchTextField+Dumb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UISearchTextField+Dumb.swift"; sourceTree = "<group>"; }; 7AC8B2752D6A01C700190706 /* UISearchTextField+Dumb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UISearchTextField+Dumb.swift"; sourceTree = "<group>"; };
7ADF6C92250B954900F237B2 /* Navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Navigation.swift; sourceTree = "<group>"; }; 7ADF6C92250B954900F237B2 /* Navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Navigation.swift; sourceTree = "<group>"; };
7ADF6C94250D037700F237B2 /* ShowEventController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowEventController.swift; sourceTree = "<group>"; };
7ADF6C96250F41B000F237B2 /* PNKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNKeyboard.swift; sourceTree = "<group>"; }; 7ADF6C96250F41B000F237B2 /* PNKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNKeyboard.swift; sourceTree = "<group>"; };
7ADF6C98250F872C00F237B2 /* RoadNumbers.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers.otf; sourceTree = "<group>"; }; 7ADF6C98250F872C00F237B2 /* RoadNumbers.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers.otf; sourceTree = "<group>"; };
7ADF6C9E251201D200F237B2 /* GlobalEventsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalEventsController.swift; sourceTree = "<group>"; };
7ADF6CA02512244400F237B2 /* MapExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapExt.swift; sourceTree = "<group>"; }; 7ADF6CA02512244400F237B2 /* MapExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapExt.swift; sourceTree = "<group>"; };
7AE24C5E251F1B4E00758E39 /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; sourceTree = "<group>"; }; 7AE24C5E251F1B4E00758E39 /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.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>"; };
@ -515,7 +521,9 @@
files = ( files = (
7AA7BC3525A5DFB80053A5D5 /* ExceptionCatcher in Frameworks */, 7AA7BC3525A5DFB80053A5D5 /* ExceptionCatcher in Frameworks */,
7AA7BC3625A5DFB80053A5D5 /* PKHUD in Frameworks */, 7AA7BC3625A5DFB80053A5D5 /* PKHUD in Frameworks */,
7A386A4B2DAC35F10051676A /* ClusterMap in Frameworks */,
7A7AA2CA2DA2C85100276D83 /* RealmSwift in Frameworks */, 7A7AA2CA2DA2C85100276D83 /* RealmSwift in Frameworks */,
7A386A4D2DAC35F10051676A /* ClusterMapSwiftUI in Frameworks */,
7AC3554A2969652F00889457 /* SwiftEntryKit in Frameworks */, 7AC3554A2969652F00889457 /* SwiftEntryKit in Frameworks */,
7ACBB91E2CB9B155005A5168 /* Mockable in Frameworks */, 7ACBB91E2CB9B155005A5168 /* Mockable in Frameworks */,
7AF6D2042677C03B0086EA64 /* AutoCatCore.framework in Frameworks */, 7AF6D2042677C03B0086EA64 /* AutoCatCore.framework in Frameworks */,
@ -647,7 +655,6 @@
7A11471423FDEAF800B424AF /* Controllers */ = { 7A11471423FDEAF800B424AF /* Controllers */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7A813DC7250B5C6E00CC93B9 /* Location */,
7A96AE2C246B2B7400297C33 /* GoogleSignInController.swift */, 7A96AE2C246B2B7400297C33 /* GoogleSignInController.swift */,
7A11471523FDEB2A00B424AF /* MainSplitController.swift */, 7A11471523FDEB2A00B424AF /* MainSplitController.swift */,
7AC3554B29696A1C00889457 /* MainTabController.swift */, 7AC3554B29696A1C00889457 /* MainTabController.swift */,
@ -718,6 +725,7 @@
7A1441632C297E9800E79018 /* Screens */ = { 7A1441632C297E9800E79018 /* Screens */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7A386A3E2DABDBFF0051676A /* MapScreen */,
7AF231912DA1C26C00AE5EB3 /* AuthScreen */, 7AF231912DA1C26C00AE5EB3 /* AuthScreen */,
7A95197E2D80B69800E69883 /* RecordsScreen */, 7A95197E2D80B69800E69883 /* RecordsScreen */,
7A5911EC2D63225500EC51BA /* SearchScreen */, 7A5911EC2D63225500EC51BA /* SearchScreen */,
@ -766,6 +774,17 @@
path = Data; path = Data;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
7A386A3E2DABDBFF0051676A /* MapScreen */ = {
isa = PBXGroup;
children = (
7A386A3F2DABDC190051676A /* MapScreen.swift */,
7A386A432DABDC360051676A /* MapViewModel.swift */,
7A386A452DABDC660051676A /* MapCoordinator.swift */,
7A386A472DABE0D00051676A /* MapMarkerModel.swift */,
);
path = MapScreen;
sourceTree = "<group>";
};
7A3F07A924360D9100E59687 /* Extensions */ = { 7A3F07A924360D9100E59687 /* Extensions */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -963,15 +982,6 @@
path = ThirdParty; path = ThirdParty;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
7A813DC7250B5C6E00CC93B9 /* Location */ = {
isa = PBXGroup;
children = (
7ADF6C94250D037700F237B2 /* ShowEventController.swift */,
7ADF6C9E251201D200F237B2 /* GlobalEventsController.swift */,
);
path = Location;
sourceTree = "<group>";
};
7A9519772D80B3B200E69883 /* AudioRecordService */ = { 7A9519772D80B3B200E69883 /* AudioRecordService */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1238,6 +1248,8 @@
7AC355492969652F00889457 /* SwiftEntryKit */, 7AC355492969652F00889457 /* SwiftEntryKit */,
7ACBB91D2CB9B155005A5168 /* Mockable */, 7ACBB91D2CB9B155005A5168 /* Mockable */,
7A7AA2C92DA2C85100276D83 /* RealmSwift */, 7A7AA2C92DA2C85100276D83 /* RealmSwift */,
7A386A4A2DAC35F10051676A /* ClusterMap */,
7A386A4C2DAC35F10051676A /* ClusterMapSwiftUI */,
); );
productName = AutoCat; productName = AutoCat;
productReference = 7A1146FD23FDE7E500B424AF /* AutoCat.app */; productReference = 7A1146FD23FDE7E500B424AF /* AutoCat.app */;
@ -1360,6 +1372,7 @@
7A1CF7FD29A41C2F007962DA /* XCRemoteSwiftPackageReference "realm-swift" */, 7A1CF7FD29A41C2F007962DA /* XCRemoteSwiftPackageReference "realm-swift" */,
7A6C4D9C2C56BCA600982597 /* XCRemoteSwiftPackageReference "SwiftLocation" */, 7A6C4D9C2C56BCA600982597 /* XCRemoteSwiftPackageReference "SwiftLocation" */,
7ACBB91C2CB9B155005A5168 /* XCRemoteSwiftPackageReference "Mockable" */, 7ACBB91C2CB9B155005A5168 /* XCRemoteSwiftPackageReference "Mockable" */,
7A386A492DAC35F10051676A /* XCRemoteSwiftPackageReference "ClusterMap" */,
); );
productRefGroup = 7A1146FE23FDE7E500B424AF /* Products */; productRefGroup = 7A1146FE23FDE7E500B424AF /* Products */;
projectDirPath = ""; projectDirPath = "";
@ -1440,9 +1453,12 @@
7AC3555029696D5A00889457 /* NewNumberController.swift in Sources */, 7AC3555029696D5A00889457 /* NewNumberController.swift in Sources */,
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */, 7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */,
7AF860702CBAA24500954D2F /* NavigationLink.swift in Sources */, 7AF860702CBAA24500954D2F /* NavigationLink.swift in Sources */,
7A386A482DABE0D00051676A /* MapMarkerModel.swift in Sources */,
7AB9FE262D08C2D7005DE374 /* EventsCoordinator.swift in Sources */, 7AB9FE262D08C2D7005DE374 /* EventsCoordinator.swift in Sources */,
7A386A402DABDC190051676A /* MapScreen.swift in Sources */,
7AB9FE282D08C2F4005DE374 /* EventsViewModel.swift in Sources */, 7AB9FE282D08C2F4005DE374 /* EventsViewModel.swift in Sources */,
7A4927D52CCE438600851C01 /* OptionalBinding.swift in Sources */, 7A4927D52CCE438600851C01 /* OptionalBinding.swift in Sources */,
7A386A462DABDC660051676A /* MapCoordinator.swift in Sources */,
7A5911EE2D63226F00EC51BA /* SearchScreen.swift in Sources */, 7A5911EE2D63226F00EC51BA /* SearchScreen.swift in Sources */,
7A17CE4A2A2E820300626A6E /* UIStackView.swift in Sources */, 7A17CE4A2A2E820300626A6E /* UIStackView.swift in Sources */,
7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */, 7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */,
@ -1466,6 +1482,7 @@
7A10226C2C551EC500B84627 /* LocationEditScreen.swift in Sources */, 7A10226C2C551EC500B84627 /* LocationEditScreen.swift in Sources */,
7A7158072C44085600852088 /* OsagoScreen.swift in Sources */, 7A7158072C44085600852088 /* OsagoScreen.swift in Sources */,
7ABD1B492D044A4700B43213 /* GalleryViewModel.swift in Sources */, 7ABD1B492D044A4700B43213 /* GalleryViewModel.swift in Sources */,
7A386A442DABDC360051676A /* MapViewModel.swift in Sources */,
7AAAFAD32C4D0FD00050410D /* ACImageSliderView.swift in Sources */, 7AAAFAD32C4D0FD00050410D /* ACImageSliderView.swift in Sources */,
7A912F372D381B7400002938 /* LicensePlateView.swift in Sources */, 7A912F372D381B7400002938 /* LicensePlateView.swift in Sources */,
7A3F07AB24360DC800E59687 /* Dated.swift in Sources */, 7A3F07AB24360DC800E59687 /* Dated.swift in Sources */,
@ -1483,7 +1500,6 @@
7A11471623FDEB2A00B424AF /* MainSplitController.swift in Sources */, 7A11471623FDEB2A00B424AF /* MainSplitController.swift in Sources */,
7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */, 7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */,
7A1022722C554A1300B84627 /* CustomHostingController.swift in Sources */, 7A1022722C554A1300B84627 /* CustomHostingController.swift in Sources */,
7ADF6C9F251201D200F237B2 /* GlobalEventsController.swift in Sources */,
7A1022792C557ED600B84627 /* LocationPickerViewModel.swift in Sources */, 7A1022792C557ED600B84627 /* LocationPickerViewModel.swift in Sources */,
7A11470323FDE7E500B424AF /* SceneDelegate.swift in Sources */, 7A11470323FDE7E500B424AF /* SceneDelegate.swift in Sources */,
7AF231932DA1C28100AE5EB3 /* AuthScreen.swift in Sources */, 7AF231932DA1C28100AE5EB3 /* AuthScreen.swift in Sources */,
@ -1510,7 +1526,6 @@
7A4955822D58CCF900912E66 /* HistoryFilter.swift in Sources */, 7A4955822D58CCF900912E66 /* HistoryFilter.swift in Sources */,
7A4322912CB2CC8A00085CF6 /* FiltersScreen.swift in Sources */, 7A4322912CB2CC8A00085CF6 /* FiltersScreen.swift in Sources */,
7ABD1B472D044A3200B43213 /* GalleryScreen.swift in Sources */, 7ABD1B472D044A3200B43213 /* GalleryScreen.swift in Sources */,
7ADF6C95250D037700F237B2 /* ShowEventController.swift in Sources */,
7A71580C2C44453200852088 /* AdsScreen.swift in Sources */, 7A71580C2C44453200852088 /* AdsScreen.swift in Sources */,
7A06E0B02C7065D8005731AC /* SettingsCoordinator.swift in Sources */, 7A06E0B02C7065D8005731AC /* SettingsCoordinator.swift in Sources */,
7A91894F29A2BD8700519C74 /* GestureRecognizers.swift in Sources */, 7A91894F29A2BD8700519C74 /* GestureRecognizers.swift in Sources */,
@ -1822,7 +1837,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 = 155; CURRENT_PROJECT_VERSION = 156;
DEVELOPMENT_TEAM = 46DTTB8X4S; DEVELOPMENT_TEAM = 46DTTB8X4S;
INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_FILE = AutoCat/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AutoCat; INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
@ -1849,7 +1864,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 = 155; CURRENT_PROJECT_VERSION = 156;
DEVELOPMENT_TEAM = 46DTTB8X4S; DEVELOPMENT_TEAM = 46DTTB8X4S;
INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_FILE = AutoCat/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AutoCat; INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
@ -2106,6 +2121,14 @@
minimumVersion = 10.36.0; minimumVersion = 10.36.0;
}; };
}; };
7A386A492DAC35F10051676A /* XCRemoteSwiftPackageReference "ClusterMap" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/vospennikov/ClusterMap.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.1.1;
};
};
7A6C4D9C2C56BCA600982597 /* XCRemoteSwiftPackageReference "SwiftLocation" */ = { 7A6C4D9C2C56BCA600982597 /* XCRemoteSwiftPackageReference "SwiftLocation" */ = {
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/malcommac/SwiftLocation.git"; repositoryURL = "https://github.com/malcommac/SwiftLocation.git";
@ -2149,6 +2172,16 @@
/* End XCRemoteSwiftPackageReference section */ /* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */ /* Begin XCSwiftPackageProductDependency section */
7A386A4A2DAC35F10051676A /* ClusterMap */ = {
isa = XCSwiftPackageProductDependency;
package = 7A386A492DAC35F10051676A /* XCRemoteSwiftPackageReference "ClusterMap" */;
productName = ClusterMap;
};
7A386A4C2DAC35F10051676A /* ClusterMapSwiftUI */ = {
isa = XCSwiftPackageProductDependency;
package = 7A386A492DAC35F10051676A /* XCRemoteSwiftPackageReference "ClusterMap" */;
productName = ClusterMapSwiftUI;
};
7A6C4D9D2C56BCA600982597 /* SwiftLocation */ = { 7A6C4D9D2C56BCA600982597 /* SwiftLocation */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = 7A6C4D9C2C56BCA600982597 /* XCRemoteSwiftPackageReference "SwiftLocation" */; package = 7A6C4D9C2C56BCA600982597 /* XCRemoteSwiftPackageReference "SwiftLocation" */;

View File

@ -1,6 +1,15 @@
{ {
"originHash" : "6fccb9fdc0d29647d4f0b927aef60f375302d72b5b724992eab52ac0d8ec71c3", "originHash" : "9a7838ec160dde976fc2028176dc43038769553e6d8eb4d7d117f851f899baaf",
"pins" : [ "pins" : [
{
"identity" : "clustermap",
"kind" : "remoteSourceControl",
"location" : "https://github.com/vospennikov/ClusterMap.git",
"state" : {
"revision" : "f3e60a742e6d3c149ce5d0c7424f373ef1b38140",
"version" : "2.1.1"
}
},
{ {
"identity" : "exceptioncatcher", "identity" : "exceptioncatcher",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="dark"/> <device id="retina4_7" orientation="portrait" appearance="dark"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -55,7 +55,7 @@
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="WxN-oK-U9s" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="WxN-oK-U9s" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="1001" y="837"/> <point key="canvasLocation" x="1823" y="143"/>
</scene> </scene>
<!--Main Tab Controller--> <!--Main Tab Controller-->
<scene sceneID="YhQ-kn-py3"> <scene sceneID="YhQ-kn-py3">
@ -69,7 +69,7 @@
</tabBarController> </tabBarController>
<placeholder placeholderIdentifier="IBFirstResponder" id="AJs-8F-Qbu" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="AJs-8F-Qbu" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="1729" y="143"/> <point key="canvasLocation" x="1018" y="143"/>
</scene> </scene>
<!--Main Split Controller--> <!--Main Split Controller-->
<scene sceneID="10H-jh-3eN"> <scene sceneID="10H-jh-3eN">
@ -83,62 +83,6 @@
</objects> </objects>
<point key="canvasLocation" x="199" y="143"/> <point key="canvasLocation" x="199" y="143"/>
</scene> </scene>
<!--Global Events Controller-->
<scene sceneID="akP-Pw-M4Q">
<objects>
<viewController storyboardIdentifier="GlobalEventsController" hidesBottomBarWhenPushed="YES" id="nFZ-L6-cSY" customClass="GlobalEventsController" customModule="AutoCat" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="qu5-Ue-dlv">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<mapView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" mapType="standard" showsCompass="NO" showsScale="YES" translatesAutoresizingMaskIntoConstraints="NO" id="q5m-0R-HGC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
</mapView>
</subviews>
<viewLayoutGuide key="safeArea" id="gve-eU-fKS"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="q5m-0R-HGC" firstAttribute="top" secondItem="qu5-Ue-dlv" secondAttribute="top" id="7pI-P3-wL4"/>
<constraint firstItem="q5m-0R-HGC" firstAttribute="leading" secondItem="qu5-Ue-dlv" secondAttribute="leading" id="P1e-Ua-97P"/>
<constraint firstAttribute="bottom" secondItem="q5m-0R-HGC" secondAttribute="bottom" id="rrd-Z6-g8h"/>
<constraint firstAttribute="trailing" secondItem="q5m-0R-HGC" secondAttribute="trailing" id="s5w-az-6e7"/>
</constraints>
</view>
<toolbarItems/>
<navigationItem key="navigationItem" id="f2h-mm-Zd0">
<barButtonItem key="rightBarButtonItem" title="Close" id="hCi-F4-z7b">
<connections>
<action selector="close:" destination="nFZ-L6-cSY" id="to7-KD-Vec"/>
</connections>
</barButtonItem>
</navigationItem>
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
<connections>
<outlet property="map" destination="q5m-0R-HGC" id="ufy-Qx-9G1"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="XB6-0a-b8N" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3554" y="143"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="fRx-9f-ao6">
<objects>
<navigationController storyboardIdentifier="GlobalEventsNavigation" automaticallyAdjustsScrollViewInsets="NO" id="HWa-Ea-ZKD" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="REm-5j-xeL">
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="nFZ-L6-cSY" kind="relationship" relationship="rootViewController" id="qjn-Wc-HGC"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Zbo-fQ-UCM" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2614" y="142"/>
</scene>
</scenes> </scenes>
<resources> <resources>
<systemColor name="systemBackgroundColor"> <systemColor name="systemBackgroundColor">

View File

@ -1,88 +0,0 @@
import UIKit
import MapKit
import PKHUD
import AutoCatCore
class EventPin: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
var id: String
init(id: String, coordinate: CLLocationCoordinate2D, title: String?, subtitle: String) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
self.id = id
}
convenience init(event: VehicleEventDto) {
let coordinate = CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude)
let address = event.address ?? "\(event.latitude), \(event.longitude)"
let date = Date(timeIntervalSince1970: event.date)
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
let dateStr = formatter.string(from: date)
if let number = event.number {
self.init(id: event.id, coordinate: coordinate, title: number, subtitle: dateStr)
} else {
self.init(id: event.id, coordinate: coordinate, title: dateStr, subtitle: address)
}
}
}
class GlobalEventsController: UIViewController {
var map: MKMapView!
var filter: Filter!
let apiService: ApiServiceProtocol = ServiceContainer.shared.resolve(ApiServiceProtocol.self)
override func viewDidLoad() {
super.viewDidLoad()
map = MKMapView()
map.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(map)
NSLayoutConstraint.activate([
map.leadingAnchor.constraint(equalTo: view.leadingAnchor),
map.trailingAnchor.constraint(equalTo: view.trailingAnchor),
map.topAnchor.constraint(equalTo: view.topAnchor),
map.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
#if targetEnvironment(macCatalyst)
if #available(OSX 11.0, *) {
self.map.showsCompass = true
}
self.map.showsZoomControls = true
#endif
Task { await loadEvents() }
}
func loadEvents() async {
do {
HUD.show(.progress)
let events = try await apiService.events(with: self.filter)
self.title = String.localizedStringWithFormat(NSLocalizedString("events found", comment: ""), events.count)
let pins = events.map(EventPin.init(event:))
self.map.removeAnnotations(self.map.annotations)
self.map.addAnnotations(pins)
self.map.centerOnPins()
HUD.hide()
} catch {
HUD.show(error: error)
}
}
@IBAction func close(_ sender: UIBarButtonItem) {
self.dismiss(animated: true, completion: nil)
}
}

View File

@ -1,28 +0,0 @@
import UIKit
import MapKit
import AutoCatCore
class ShowEventController: UIViewController {
private var map = MKMapView()
var event: VehicleEventDto?
override func viewDidLoad() {
super.viewDidLoad()
self.map.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(self.map)
self.map.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.map.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
self.map.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
self.map.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
if let event = self.event {
self.title = event.address ?? String(format: "%.02f, %.02f", event.latitude, event.longitude)
let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude), latitudinalMeters: 1000, longitudinalMeters: 1000)
self.map.setRegion(region, animated: true)
let pin = EventPin(event: event)
self.map.addAnnotation(pin)
}
}
}

View File

@ -0,0 +1,45 @@
//
// MapCoordinator.swift
// AutoCat
//
// Created by Selim Mustafaev on 13.04.2025.
// Copyright © 2025 Selim Mustafaev. All rights reserved.
//
import UIKit
import SwiftUI
import AutoCatCore
enum MapInput {
case event(VehicleEventDto)
case filter(Filter)
}
@MainActor
final class MapCoordinator {
let navController: UINavigationController
init(navController: UINavigationController) {
self.navController = navController
}
func start(mapInput: MapInput) {
let resolver = ServiceContainer.shared
let viewModel = MapViewModel(
apiService: resolver.resolve(ApiServiceProtocol.self),
mapInput: mapInput
)
let controller = UIHostingController(
rootView: MapScreen(viewModel: viewModel)
)
//controller.modalPresentationStyle = .fullScreen
controller.hidesBottomBarWhenPushed = true
navController.pushViewController(controller, animated: true)
}
}

View File

@ -0,0 +1,17 @@
//
// MapMarkerModel.swift
// AutoCat
//
// Created by Selim Mustafaev on 13.04.2025.
// Copyright © 2025 Selim Mustafaev. All rights reserved.
//
import CoreLocation
import ClusterMap
struct MapMarkerModel: CoordinateIdentifiable, Identifiable, Hashable {
var id = UUID()
var title: String
var coordinate: CLLocationCoordinate2D
}

View File

@ -0,0 +1,43 @@
//
// MapScreen.swift
// AutoCat
//
// Created by Selim Mustafaev on 13.04.2025.
// Copyright © 2025 Selim Mustafaev. All rights reserved.
//
import SwiftUI
import MapKit
import ClusterMapSwiftUI
struct MapScreen: View {
@State var viewModel: MapViewModel
var body: some View {
Map {
ForEach(viewModel.markers) {
Marker($0.title, coordinate: $0.coordinate)
}
ForEach(viewModel.clusters) {
Marker($0.title, coordinate: $0.coordinate)
}
}
.onAppear {
Task { await viewModel.onAppear() }
}
.hud($viewModel.hud)
.navigationTitle(viewModel.title)
.readSize { newValue in
viewModel.mapSize = newValue
}
.onMapCameraChange { context in
viewModel.currentRegion = context.region
}
.onMapCameraChange(frequency: .onEnd) { context in
Task.detached {
await viewModel.reloadMarkers()
}
}
}
}

View File

@ -0,0 +1,100 @@
//
// MapViewModel.swift
// AutoCat
//
// Created by Selim Mustafaev on 13.04.2025.
// Copyright © 2025 Selim Mustafaev. All rights reserved.
//
import SwiftUI
import AutoCatCore
import CoreLocation
import MapKit
import ClusterMap
@MainActor
@Observable
final class MapViewModel: ACHudContainer {
let apiService: ApiServiceProtocol
let mapInput: MapInput
var hud: ACHud?
var title: String = ""
var markers: [MapMarkerModel] = []
var clusters: [MapMarkerModel] = []
let clusterManager = ClusterManager<MapMarkerModel>()
var mapSize: CGSize = .zero
var currentRegion: MKCoordinateRegion = .init()
init(apiService: ApiServiceProtocol, mapInput: MapInput) {
self.apiService = apiService
self.mapInput = mapInput
}
func onAppear() async {
switch mapInput {
case .event(let event):
markers = [makeMarkerModel(from: event)]
case .filter(let filter):
await loadEvents(with: filter)
}
}
func makeMarkerModel(from event: VehicleEventDto) -> MapMarkerModel {
let date = Date(timeIntervalSince1970: event.date)
let dateString = Formatters.marker.string(from: date)
let coordinate = CLLocationCoordinate2DMake(event.latitude, event.longitude)
return .init(
title: dateString,
coordinate: coordinate
)
}
func loadEvents(with filter: Filter) async {
await wrapWithToast { [weak self] in
guard let self else { return }
let events = try await apiService.events(with: filter)
let markers = events.map(makeMarkerModel)
await clusterManager.add(markers)
await reloadMarkers()
}
}
nonisolated func reloadMarkers() async {
async let changes = clusterManager.reload(
mapViewSize: mapSize,
coordinateRegion: currentRegion
)
await applyChanges(changes)
}
private func applyChanges(_ difference: ClusterManager<MapMarkerModel>.Difference) {
for removal in difference.removals {
switch removal {
case .annotation(let annotation):
markers.removeAll { $0 == annotation }
case .cluster(let clusterAnnotation):
clusters.removeAll { $0.id == clusterAnnotation.id }
}
}
for insertion in difference.insertions {
switch insertion {
case .annotation(let newItem):
markers.append(newItem)
case .cluster(let newItem):
clusters.append(MapMarkerModel(
id: newItem.id,
title: String(newItem.memberAnnotations.count),
coordinate: newItem.coordinate
))
}
}
}
}

View File

@ -37,9 +37,7 @@ final class RecordsCoordinator {
func showOnMap(event: VehicleEventDto) { func showOnMap(event: VehicleEventDto) {
let controller = ShowEventController() let coordinator = MapCoordinator(navController: navController)
controller.event = event coordinator.start(mapInput: .event(event))
controller.hidesBottomBarWhenPushed = true
navController.pushViewController(controller, animated: true)
} }
} }

View File

@ -48,10 +48,8 @@ final class SearchCoordinator {
func showOnMap(filter: Filter) { func showOnMap(filter: Filter) {
let controller = GlobalEventsController() let coordinator = MapCoordinator(navController: navController)
controller.filter = filter coordinator.start(mapInput: .filter(filter))
controller.modalPresentationStyle = .fullScreen
navController.pushViewController(controller, animated: true)
} }
func export(url: URL) { func export(url: URL) {