Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b12f83680a | |||
| ee827c41ae | |||
| d0d06394f0 | |||
| b933b900ed | |||
| 0e7de44332 | |||
| 547bb794f0 | |||
| 9b1f1f12d2 | |||
| 7fc324367d | |||
| 3cb712f709 | |||
| 993e2924c7 | |||
| a167c989cc | |||
| 1b50f4fd17 | |||
| 0003b4607c | |||
| 85c9f333ce | |||
| 217cbfd4e3 | |||
| 5d8de1fb36 | |||
| f23bb78ceb | |||
| 0d91954614 | |||
| 49f5fa91fe | |||
| 68e560768b | |||
| 3e9c319b50 | |||
| d35e0e1b4a | |||
| 6ede137ef7 | |||
| c94195d48e | |||
| 28919d7b72 | |||
| a239534b91 | |||
| 226342f36c | |||
| ca6afbec5f |
@@ -1,5 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## Versione 5.9
|
||||
- Aggiunta barra laterale in lista sismi
|
||||
- Aggiunto filtro "sismi percepiti"
|
||||
- Aggiunta "Mappa intensità"
|
||||
|
||||
## Versione 5.8.1
|
||||
- Corrette traduzioni errate (causavano crash)
|
||||
- Aggiunto ordinamento in sottoscrizioni utente (prima Top10k)
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
650247122A61832F001AC512 /* EQNBackgroundPositionDebugHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650247112A61832F001AC512 /* EQNBackgroundPositionDebugHelper.swift */; };
|
||||
650247152A618D7F001AC512 /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650247142A618D7F001AC512 /* Foundation+Extensions.swift */; };
|
||||
650B23AB2632CCD3007AE752 /* UIView+EQNExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650B23AA2632CCD3007AE752 /* UIView+EQNExtensions.swift */; };
|
||||
6514FF6A2D720C3F000A7BD0 /* MapPinStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6514FF692D720C3A000A7BD0 /* MapPinStyle.swift */; };
|
||||
6514FF6C2D720CBE000A7BD0 /* MapPinStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6514FF692D720C3A000A7BD0 /* MapPinStyle.swift */; };
|
||||
6514FF6D2D720CBE000A7BD0 /* MapPinStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6514FF692D720C3A000A7BD0 /* MapPinStyle.swift */; };
|
||||
65172F532C25C496006D2A5C /* EQNSeismicAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65172F522C25C496006D2A5C /* EQNSeismicAnnotationView.swift */; };
|
||||
651901B925F5358700CAFF20 /* EQNMapAnnotationSeismic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 651901B825F5358700CAFF20 /* EQNMapAnnotationSeismic.swift */; };
|
||||
6525A82625E13FD4008FE0D0 /* SeismicNetworkAdvertiseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6525A82525E13FD4008FE0D0 /* SeismicNetworkAdvertiseTableViewCell.swift */; };
|
||||
@@ -41,6 +44,9 @@
|
||||
656E02162C1C4DF2008D0E92 /* EQNBaseContainerTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 656E02152C1C4DF2008D0E92 /* EQNBaseContainerTableViewCell.swift */; };
|
||||
656EB9362A15FD16009DADF3 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 656EB9382A15FD16009DADF3 /* Localizable.stringsdict */; };
|
||||
656EB9412A16288A009DADF3 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 656EB9382A15FD16009DADF3 /* Localizable.stringsdict */; };
|
||||
657415E02D70B6F800F54890 /* EQNMapAnnotationShakemap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657415DE2D70B6F700F54890 /* EQNMapAnnotationShakemap.swift */; };
|
||||
657747FD2D4D12A200213830 /* SeismicNetworkData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657747FC2D4D12A200213830 /* SeismicNetworkData.swift */; };
|
||||
657747FF2D4D2BA200213830 /* SeismicNetworkScrollIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657747FE2D4D2BA100213830 /* SeismicNetworkScrollIndicatorView.swift */; };
|
||||
6586971125F44C26009C0182 /* EQNBlurredCloseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6586971025F44C26009C0182 /* EQNBlurredCloseButton.swift */; };
|
||||
658BAB7B25FE67930015C454 /* EQNBaseMapRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658BAB7A25FE67930015C454 /* EQNBaseMapRepresentable.swift */; };
|
||||
658BC0292859A456009EECAA /* RealtimeAlertViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658BC0282859A456009EECAA /* RealtimeAlertViewController.swift */; };
|
||||
@@ -73,6 +79,10 @@
|
||||
65E6AC702C2DB3B60073F8FE /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 65E6AC6F2C2DB3B60073F8FE /* FirebaseMessaging */; };
|
||||
65EA58802A60269C0038EE9D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8C10B0BD2281FE7F00125C9F /* Localizable.strings */; };
|
||||
65EA58822A60360D0038EE9D /* EQNRealtimePushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65EA58812A60360D0038EE9D /* EQNRealtimePushNotification.swift */; };
|
||||
65F9A6082D706592008A12B5 /* SeismicNetworksIntensityMapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9A6072D706592008A12B5 /* SeismicNetworksIntensityMapViewController.swift */; };
|
||||
65F9A60A2D706ADC008A12B5 /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9A6092D706ADC008A12B5 /* APIService.swift */; };
|
||||
65F9A60C2D70781A008A12B5 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9A60B2D70781A008A12B5 /* Log.swift */; };
|
||||
65F9A60E2D707BFE008A12B5 /* EQNShakemap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9A60D2D707BFE008A12B5 /* EQNShakemap.swift */; };
|
||||
65F9B49C2A8CA22800F13260 /* BackgroundTaskManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9B49B2A8CA22800F13260 /* BackgroundTaskManager.swift */; };
|
||||
65F9B49E2A8CA2AC00F13260 /* EQNBackgroundPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9B49D2A8CA2AC00F13260 /* EQNBackgroundPosition.swift */; };
|
||||
65F9B4A02A8CC58200F13260 /* UpdateUserLocationTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9B49F2A8CC58200F13260 /* UpdateUserLocationTask.swift */; };
|
||||
@@ -307,6 +317,7 @@
|
||||
650247112A61832F001AC512 /* EQNBackgroundPositionDebugHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBackgroundPositionDebugHelper.swift; sourceTree = "<group>"; };
|
||||
650247142A618D7F001AC512 /* Foundation+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Foundation+Extensions.swift"; sourceTree = "<group>"; };
|
||||
650B23AA2632CCD3007AE752 /* UIView+EQNExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+EQNExtensions.swift"; sourceTree = "<group>"; };
|
||||
6514FF692D720C3A000A7BD0 /* MapPinStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapPinStyle.swift; sourceTree = "<group>"; };
|
||||
65172F522C25C496006D2A5C /* EQNSeismicAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNSeismicAnnotationView.swift; sourceTree = "<group>"; };
|
||||
651901B825F5358700CAFF20 /* EQNMapAnnotationSeismic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNMapAnnotationSeismic.swift; sourceTree = "<group>"; };
|
||||
6525A82525E13FD4008FE0D0 /* SeismicNetworkAdvertiseTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworkAdvertiseTableViewCell.swift; sourceTree = "<group>"; };
|
||||
@@ -338,6 +349,9 @@
|
||||
656EB93E2A15FD1E009DADF3 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = it; path = it.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
656EB93F2A15FD1F009DADF3 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
656EB9402A15FD1F009DADF3 /* tr-TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "tr-TR"; path = "tr-TR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
657415DE2D70B6F700F54890 /* EQNMapAnnotationShakemap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNMapAnnotationShakemap.swift; sourceTree = "<group>"; };
|
||||
657747FC2D4D12A200213830 /* SeismicNetworkData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworkData.swift; sourceTree = "<group>"; };
|
||||
657747FE2D4D2BA100213830 /* SeismicNetworkScrollIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworkScrollIndicatorView.swift; sourceTree = "<group>"; };
|
||||
6586971025F44C26009C0182 /* EQNBlurredCloseButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBlurredCloseButton.swift; sourceTree = "<group>"; };
|
||||
658BAB7A25FE67930015C454 /* EQNBaseMapRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBaseMapRepresentable.swift; sourceTree = "<group>"; };
|
||||
658BC0282859A456009EECAA /* RealtimeAlertViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealtimeAlertViewController.swift; sourceTree = "<group>"; };
|
||||
@@ -373,6 +387,10 @@
|
||||
65DBFB7225E2BBF20041CBA6 /* GADTTemplateView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GADTTemplateView.m; sourceTree = "<group>"; };
|
||||
65DBFB7325E2BBF20041CBA6 /* GADTMediumTemplateView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GADTMediumTemplateView.h; sourceTree = "<group>"; };
|
||||
65EA58812A60360D0038EE9D /* EQNRealtimePushNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNRealtimePushNotification.swift; sourceTree = "<group>"; };
|
||||
65F9A6072D706592008A12B5 /* SeismicNetworksIntensityMapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworksIntensityMapViewController.swift; sourceTree = "<group>"; };
|
||||
65F9A6092D706ADC008A12B5 /* APIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = "<group>"; };
|
||||
65F9A60B2D70781A008A12B5 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
|
||||
65F9A60D2D707BFE008A12B5 /* EQNShakemap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNShakemap.swift; sourceTree = "<group>"; };
|
||||
65F9B49B2A8CA22800F13260 /* BackgroundTaskManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskManager.swift; sourceTree = "<group>"; };
|
||||
65F9B49D2A8CA2AC00F13260 /* EQNBackgroundPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBackgroundPosition.swift; sourceTree = "<group>"; };
|
||||
65F9B49F2A8CC58200F13260 /* UpdateUserLocationTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateUserLocationTask.swift; sourceTree = "<group>"; };
|
||||
@@ -663,8 +681,10 @@
|
||||
65DBFB5225E2A2580041CBA6 /* Map annotation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6514FF692D720C3A000A7BD0 /* MapPinStyle.swift */,
|
||||
653C67FB25F3D63500FE52AC /* EQNMapAnnotationUserReport.swift */,
|
||||
651901B825F5358700CAFF20 /* EQNMapAnnotationSeismic.swift */,
|
||||
657415DE2D70B6F700F54890 /* EQNMapAnnotationShakemap.swift */,
|
||||
654D18C825F93CD700BB6DB0 /* EQNMapAnnotationPastquake.swift */,
|
||||
);
|
||||
path = "Map annotation";
|
||||
@@ -912,12 +932,15 @@
|
||||
DC141968250E769B0059E060 /* Seismic Networks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6562C80625FFA6B100C85273 /* SeismicNetworkViewModel.swift */,
|
||||
DC28142E2519C21400C1AFF7 /* Cells */,
|
||||
65F0857E2609288E0002615C /* Filters */,
|
||||
6562C80625FFA6B100C85273 /* SeismicNetworkViewModel.swift */,
|
||||
657747FC2D4D12A200213830 /* SeismicNetworkData.swift */,
|
||||
657747FE2D4D2BA100213830 /* SeismicNetworkScrollIndicatorView.swift */,
|
||||
DC2814372519C56100C1AFF7 /* SeismicNetworksViewController.swift */,
|
||||
DCC76BD7251F56050005C4DC /* SeismicCardSettingsViewController.swift */,
|
||||
65DBFB4A25E29DD60041CBA6 /* SeismicNetworksMapDetailViewController.swift */,
|
||||
65F9A6072D706592008A12B5 /* SeismicNetworksIntensityMapViewController.swift */,
|
||||
);
|
||||
path = "Seismic Networks";
|
||||
sourceTree = "<group>";
|
||||
@@ -987,6 +1010,7 @@
|
||||
DC10563F251E7EC0002579BB /* Extensions */,
|
||||
8CF66054214C566A009F4314 /* Reachability.h */,
|
||||
8CF66056214C566A009F4314 /* Reachability.m */,
|
||||
65F9A60B2D70781A008A12B5 /* Log.swift */,
|
||||
);
|
||||
path = Libs;
|
||||
sourceTree = "<group>";
|
||||
@@ -1042,6 +1066,7 @@
|
||||
8C4E344A2152EE5B008B0D2A /* EQNGeneratoreURLServer.m */,
|
||||
8CF66057214C566B009F4314 /* ServerRequest.h */,
|
||||
8CF66055214C566A009F4314 /* ServerRequest.m */,
|
||||
65F9A6092D706ADC008A12B5 /* APIService.swift */,
|
||||
);
|
||||
path = "Server Requests";
|
||||
sourceTree = "<group>";
|
||||
@@ -1113,6 +1138,7 @@
|
||||
65EA58812A60360D0038EE9D /* EQNRealtimePushNotification.swift */,
|
||||
6552C1452926DBA1008E723C /* AppPreferences.swift */,
|
||||
6590EFF72C3004EA00F41420 /* EQNOfficialPushNotification.swift */,
|
||||
65F9A60D2D707BFE008A12B5 /* EQNShakemap.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
@@ -1290,8 +1316,9 @@
|
||||
8CBD3DBA2149B9AD0070C963 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastSwiftUpdateCheck = 1410;
|
||||
LastUpgradeCheck = 1410;
|
||||
LastUpgradeCheck = 1620;
|
||||
ORGANIZATIONNAME = "Earthquake Network";
|
||||
TargetAttributes = {
|
||||
65FFDC91292F672B00EA821B = {
|
||||
@@ -1531,6 +1558,7 @@
|
||||
files = (
|
||||
65FFDC95292F672B00EA821B /* NotificationService.swift in Sources */,
|
||||
6502470E2A6136F0001AC512 /* Constants.swift in Sources */,
|
||||
6514FF6D2D720CBE000A7BD0 /* MapPinStyle.swift in Sources */,
|
||||
6502470F2A613AD7001AC512 /* EQNUserData.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -1559,6 +1587,7 @@
|
||||
DC03BEAB250BC0A60084769B /* EQNRoundedButton.swift in Sources */,
|
||||
65AD23CE261B03D400E3B57C /* SubscriptionsDescriptionTableViewCell.swift in Sources */,
|
||||
DCD4571C24F6CF0D00B58304 /* EQNGenericValue.swift in Sources */,
|
||||
657747FD2D4D12A200213830 /* SeismicNetworkData.swift in Sources */,
|
||||
8C4E343F215012FA008B0D2A /* EQNManager.m in Sources */,
|
||||
DCAA913F24F68A1D00145A3D /* SettingMultivaluesTableViewCell.swift in Sources */,
|
||||
DCF9E14F24F6EA07002B6B1D /* EQNSeismicNetwork.swift in Sources */,
|
||||
@@ -1571,6 +1600,7 @@
|
||||
6562C80725FFA6B100C85273 /* SeismicNetworkViewModel.swift in Sources */,
|
||||
DCDE0BD924E58CCE00209778 /* EQNMainTabBarController.m in Sources */,
|
||||
8C4E344B2152EE5B008B0D2A /* EQNGeneratoreURLServer.m in Sources */,
|
||||
65F9A60A2D706ADC008A12B5 /* APIService.swift in Sources */,
|
||||
6590EFF82C3004EA00F41420 /* EQNOfficialPushNotification.swift in Sources */,
|
||||
DC99A50724E66E5F0071BC9F /* EQNStartupCommandsBuilder.swift in Sources */,
|
||||
8CF66058214C566B009F4314 /* ServerRequest.m in Sources */,
|
||||
@@ -1582,6 +1612,7 @@
|
||||
DC886A5D24E92D5500F7A5D3 /* EQNBaseViewController.m in Sources */,
|
||||
8C593E8A217BA2470008B260 /* EQNSegnalazione.m in Sources */,
|
||||
8CBD3DCA2149B9AD0070C963 /* AllerteViewController.m in Sources */,
|
||||
65F9A60E2D707BFE008A12B5 /* EQNShakemap.swift in Sources */,
|
||||
DC646F32252B698B000AA5FD /* AlertsSeismicNotificationCompactTableViewCell.swift in Sources */,
|
||||
DCF10DCD24D2C935009F34C3 /* EQNPurchaseAvailability.swift in Sources */,
|
||||
6552C1462926DBA1008E723C /* AppPreferences.swift in Sources */,
|
||||
@@ -1596,6 +1627,7 @@
|
||||
8CF6604F214C0E58009F4314 /* EQNCalibrazione.m in Sources */,
|
||||
65355FFF25F38D3300BB57D2 /* SegnalazioniMapViewController.swift in Sources */,
|
||||
DCBB84F0252CFC4600F12633 /* AlertsNoLocationTableViewCell.swift in Sources */,
|
||||
657747FF2D4D2BA200213830 /* SeismicNetworkScrollIndicatorView.swift in Sources */,
|
||||
651901B925F5358700CAFF20 /* EQNMapAnnotationSeismic.swift in Sources */,
|
||||
DC99A50324E66E270071BC9F /* EQNCommandProtocol.swift in Sources */,
|
||||
DCF10DC624D2B8C7009F34C3 /* EQNPurchaseUtility.swift in Sources */,
|
||||
@@ -1604,6 +1636,7 @@
|
||||
65BBB22C26064BE6005D6CDF /* SegnalazioniLast24HoursCell.swift in Sources */,
|
||||
DC47D1BC252A0C2B004119F6 /* AlertsPastEartquakesTableViewCell.swift in Sources */,
|
||||
654D18C925F93CD700BB6DB0 /* EQNMapAnnotationPastquake.swift in Sources */,
|
||||
6514FF6A2D720C3F000A7BD0 /* MapPinStyle.swift in Sources */,
|
||||
DCEFF21724F58569009D3FE1 /* SettingSectionHeaderView.swift in Sources */,
|
||||
65DB60FD2C172C4A00164366 /* EQNSettingRealTimeAlert.swift in Sources */,
|
||||
DCBB267A24D1E7F500F04559 /* SubscriptionsHeaderTableViewCell.swift in Sources */,
|
||||
@@ -1622,11 +1655,13 @@
|
||||
DC7EEE4A252A11C9004B4A2A /* AlertsSmartphoneNetworkTableViewCell.swift in Sources */,
|
||||
DC7EEE4F252A1634004B4A2A /* AlertsPriorityServiceTableViewCell.swift in Sources */,
|
||||
653604E9262348FA00B2B651 /* EQNBaseMapFilter.swift in Sources */,
|
||||
65F9A60C2D70781A008A12B5 /* Log.swift in Sources */,
|
||||
65583A05261B83BE00ECA9F9 /* UIKit+Extensions.swift in Sources */,
|
||||
65DBFB7625E2BBF20041CBA6 /* GADTTemplateView.m in Sources */,
|
||||
8C5EA23D2177B51C002DC156 /* SegnalazioniViewController.m in Sources */,
|
||||
656E02162C1C4DF2008D0E92 /* EQNBaseContainerTableViewCell.swift in Sources */,
|
||||
656D138F2C2225560094F597 /* SubscriptionDetailsViewController.swift in Sources */,
|
||||
65F9A6082D706592008A12B5 /* SeismicNetworksIntensityMapViewController.swift in Sources */,
|
||||
653C67E225F3CC2E00FE52AC /* EQNCustomAnnotationView.swift in Sources */,
|
||||
8CF4F4D8216D3A110057110B /* EQNAreaCheck.m in Sources */,
|
||||
8C4E34452152B707008B0D2A /* EQNAccelerometroManager.m in Sources */,
|
||||
@@ -1649,6 +1684,7 @@
|
||||
8C8EBBA721540039002784BA /* EQNUser.m in Sources */,
|
||||
8CADAA9421B2627D0044E256 /* EQNLogViewController.m in Sources */,
|
||||
DC3BA11124D1A9C90062EE7F /* SubscriptionsViewController.swift in Sources */,
|
||||
657415E02D70B6F800F54890 /* EQNMapAnnotationShakemap.swift in Sources */,
|
||||
DC646F27252B694A000AA5FD /* AlertsSeismicNotificationExpandedTableViewCell.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -1669,6 +1705,7 @@
|
||||
654D18DA25F9424700BB6DB0 /* EQNUtility+Extensions.swift in Sources */,
|
||||
DC0AE1BA2538204100111307 /* EQNPastquakes.m in Sources */,
|
||||
65D9938A29219DEC00F2B0EB /* UIKit+Extensions.swift in Sources */,
|
||||
6514FF6C2D720CBE000A7BD0 /* MapPinStyle.swift in Sources */,
|
||||
8CF12CD921DE49B600613AC5 /* NotificationViewController.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -1773,7 +1810,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 139;
|
||||
CURRENT_PROJECT_VERSION = 146;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = WJA4MR4CPC;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
@@ -1790,7 +1827,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.8.2;
|
||||
MARKETING_VERSION = 5.9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.finazzi.distquake.notificationservice;
|
||||
@@ -1815,7 +1852,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Distribution";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 139;
|
||||
CURRENT_PROJECT_VERSION = 146;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = WJA4MR4CPC;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1828,7 +1865,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.8.2;
|
||||
MARKETING_VERSION = 5.9.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.finazzi.distquake.notificationservice;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1964,16 +2001,16 @@
|
||||
8CBD3DDC2149B9AD0070C963 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "Earthquake Network/Earthquake Network.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 139;
|
||||
CURRENT_PROJECT_VERSION = 146;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = WJA4MR4CPC;
|
||||
ENABLE_MODULE_VERIFIER = YES;
|
||||
GCC_PREFIX_HEADER = "Earthquake Network/Earthquake Network-Prefix.pch";
|
||||
INFOPLIST_FILE = "Earthquake Network/Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
@@ -1981,7 +2018,8 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.8.2;
|
||||
MARKETING_VERSION = 5.9.0;
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -2051,15 +2089,15 @@
|
||||
8CBD3DDD2149B9AD0070C963 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "Earthquake Network/Earthquake Network.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 139;
|
||||
CURRENT_PROJECT_VERSION = 146;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = WJA4MR4CPC;
|
||||
ENABLE_MODULE_VERIFIER = YES;
|
||||
GCC_PREFIX_HEADER = "Earthquake Network/Earthquake Network-Prefix.pch";
|
||||
INFOPLIST_FILE = "Earthquake Network/Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
@@ -2067,7 +2105,8 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.8.2;
|
||||
MARKETING_VERSION = 5.9.0;
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -2140,7 +2179,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = EQNNotificationContent/EQNNotificationContent.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 139;
|
||||
CURRENT_PROJECT_VERSION = 146;
|
||||
DEVELOPMENT_TEAM = WJA4MR4CPC;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
@@ -2155,7 +2194,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.8.2;
|
||||
MARKETING_VERSION = 5.9.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.finazzi.distquake.notificationcontent;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "EQNetwork Extension Content - Development";
|
||||
@@ -2174,7 +2213,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = EQNNotificationContent/EQNNotificationContent.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 139;
|
||||
CURRENT_PROJECT_VERSION = 146;
|
||||
DEVELOPMENT_TEAM = WJA4MR4CPC;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"ADS_ENABLED=0",
|
||||
@@ -2187,7 +2226,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.8.2;
|
||||
MARKETING_VERSION = 5.9.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.finazzi.distquake.notificationcontent;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "EQNetwork Extension Content - AppStore";
|
||||
@@ -2242,7 +2281,7 @@
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
6552C13629262119008E723C /* XCRemoteSwiftPackageReference "Shogun" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/andreabusi-it/Shogun";
|
||||
repositoryURL = "https://github.com/andreabusi-it/Shogun.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 1.0.0;
|
||||
@@ -2269,7 +2308,7 @@
|
||||
repositoryURL = "https://github.com/googleads/swift-package-manager-google-mobile-ads.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 11.3.0;
|
||||
minimumVersion = 12.0.0;
|
||||
};
|
||||
};
|
||||
65B16E282BDFB39B0020527E /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */ = {
|
||||
@@ -2277,7 +2316,7 @@
|
||||
repositoryURL = "https://github.com/facebook/facebook-ios-sdk";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 14.1.0;
|
||||
minimumVersion = 18.0.0;
|
||||
};
|
||||
};
|
||||
65E6AC6C2C2DB3B60073F8FE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
|
||||
@@ -2285,7 +2324,7 @@
|
||||
repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 10.28.1;
|
||||
minimumVersion = 11.1.0;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
+19
-19
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"originHash" : "c29b9b16ee6b4d1a6fec2debc59749097860256c8cbb2addc2abc08d3adba59d",
|
||||
"originHash" : "898a30d298491e1ce821191ebfa2e7d28e7c9ea4c119cbfdfb1b245bc94ac6c3",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "abseil-cpp-binary",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/abseil-cpp-binary.git",
|
||||
"state" : {
|
||||
"revision" : "748c7837511d0e6a507737353af268484e1745e2",
|
||||
"version" : "1.2024011601.1"
|
||||
"revision" : "194a6706acbd25e4ef639bcaddea16e8758a3e27",
|
||||
"version" : "1.2024011602.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -15,8 +15,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/app-check.git",
|
||||
"state" : {
|
||||
"revision" : "3b62f154d00019ae29a71e9738800bb6f18b236d",
|
||||
"version" : "10.19.2"
|
||||
"revision" : "61b85103a1aeed8218f17c794687781505fbbef5",
|
||||
"version" : "11.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -33,8 +33,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/facebook/facebook-ios-sdk",
|
||||
"state" : {
|
||||
"revision" : "c19607d535864533523d1f437c84035e5fb101cf",
|
||||
"version" : "14.1.0"
|
||||
"revision" : "b28dde427715b45a26ebebf697929f4a81b15e04",
|
||||
"version" : "18.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -42,8 +42,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/firebase-ios-sdk.git",
|
||||
"state" : {
|
||||
"revision" : "eca84fd638116dd6adb633b5a3f31cc7befcbb7d",
|
||||
"version" : "10.29.0"
|
||||
"revision" : "075679d6b25b28f4cb167f1d7769b58fb556fb30",
|
||||
"version" : "11.8.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -51,8 +51,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleAppMeasurement.git",
|
||||
"state" : {
|
||||
"revision" : "fe727587518729046fc1465625b9afd80b5ab361",
|
||||
"version" : "10.28.0"
|
||||
"revision" : "be0881ff728eca210ccb628092af400c086abda3",
|
||||
"version" : "11.7.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -60,8 +60,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleDataTransport.git",
|
||||
"state" : {
|
||||
"revision" : "a637d318ae7ae246b02d7305121275bc75ed5565",
|
||||
"version" : "9.4.0"
|
||||
"revision" : "617af071af9aa1d6a091d59a202910ac482128f9",
|
||||
"version" : "10.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -69,8 +69,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleUtilities.git",
|
||||
"state" : {
|
||||
"revision" : "57a1d307f42df690fdef2637f3e5b776da02aad6",
|
||||
"version" : "7.13.3"
|
||||
"revision" : "53156c7ec267db846e6b64c9f4c4e31ba4cf75eb",
|
||||
"version" : "8.0.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -78,8 +78,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/grpc-binary.git",
|
||||
"state" : {
|
||||
"revision" : "e9fad491d0673bdda7063a0341fb6b47a30c5359",
|
||||
"version" : "1.62.2"
|
||||
"revision" : "f56d8fc3162de9a498377c7b6cea43431f4f5083",
|
||||
"version" : "1.65.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -150,8 +150,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/googleads/swift-package-manager-google-mobile-ads.git",
|
||||
"state" : {
|
||||
"revision" : "9ab66e38f5f0c2d02f2b024b1babd880130f19bf",
|
||||
"version" : "11.3.0"
|
||||
"revision" : "aa24c7dc03bca62c42747314dc8537f15587b50d",
|
||||
"version" : "12.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1410"
|
||||
LastUpgradeVersion = "1620"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1410"
|
||||
LastUpgradeVersion = "1620"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
@@ -15,7 +15,7 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8C4B0B7921CACE3F00AED489"
|
||||
BlueprintIdentifier = "65FFDC91292F672B00EA821B"
|
||||
BuildableName = "EQNNotificationService.appex"
|
||||
BlueprintName = "EQNNotificationService"
|
||||
ReferencedContainer = "container:Earthquake Network.xcodeproj">
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1410"
|
||||
LastUpgradeVersion = "1620"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -212,14 +212,14 @@
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
|
||||
if (status == ATTrackingManagerAuthorizationStatusAuthorized) {
|
||||
[FBSDKSettings.sharedSettings setAdvertiserTrackingEnabled:YES];
|
||||
FBSDKSettings.sharedSettings.isAdvertiserTrackingEnabled = YES;
|
||||
} else {
|
||||
[FBSDKSettings.sharedSettings setAdvertiserTrackingEnabled:NO];
|
||||
FBSDKSettings.sharedSettings.isAdvertiserTrackingEnabled = NO;
|
||||
}
|
||||
}];
|
||||
});
|
||||
} else {
|
||||
[FBSDKSettings.sharedSettings setAdvertiserTrackingEnabled:YES];
|
||||
FBSDKSettings.sharedSettings.isAdvertiserTrackingEnabled = YES;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@
|
||||
- (void)configureFacebookSDKWithApplication:(UIApplication *)application andOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
[FBSDKApplicationDelegate.sharedInstance application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
[FBSDKSettings.sharedSettings setAdvertiserIDCollectionEnabled:YES];
|
||||
FBSDKSettings.sharedSettings.isAdvertiserIDCollectionEnabled = YES;
|
||||
}
|
||||
|
||||
#pragma mark - FIRMessagingDelegate
|
||||
|
||||
@@ -56,13 +56,16 @@ extension UserDefaults {
|
||||
static let UserReportExpandedView = "EQNData.UserReportExpandedView"
|
||||
/// Se `true` visualizza le opzioni nella singole card in reti sismiche
|
||||
static let AlertsShowCardOptions = "EQNetwork.AlertsShowAllCards"
|
||||
/// Indica lo stile di pin da visualizzare nelle mappe
|
||||
static let MapPinStyle = "EQNetwork.MapPinStyle"
|
||||
|
||||
// Migrazioni
|
||||
static let AppMigrationV5_3 = "EQNUserDefaultMigrationV5_3"
|
||||
static let AppMigrationV5_4 = "EQNUserDefaultMigrationV5_4"
|
||||
static let AppMigrationV5_8 = "EQNUserDefaultMigrationV5_8"
|
||||
static let AppMigrationV5_8_2 = "EQNUserDefaultMigrationV5_8_2"
|
||||
|
||||
static let AppMigrationV5_9 = "EQNUserDefaultMigrationV5_9"
|
||||
|
||||
static let SettingsSeismicNetworkNotificationMigrationV5_8 = "EQNUserDefaultSettingsSeismicNetworkNotificationMigrationV5_8"
|
||||
static let SettingsUserReportNotificationMigrationV5_8 = "EQNUserDefaultSettingsUserReportNotificationMigrationV5_8"
|
||||
static let SismicFiltersMigrationV5_8 = "EQNUserDefaultSismicFiltersMigrationV5_8"
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ class SeismicNetworkAdvertiseTableViewCell: UITableViewCell {
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var bannerView: GADNativeAdView = {
|
||||
private lazy var bannerView: NativeAdView = {
|
||||
let view = GADTMediumTemplateView()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
return view
|
||||
@@ -71,7 +71,7 @@ class SeismicNetworkAdvertiseTableViewCell: UITableViewCell {
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func loadNativeAd(_ nativeAd: GADNativeAd) {
|
||||
func loadNativeAd(_ nativeAd: NativeAd) {
|
||||
bannerView.nativeAd = nativeAd
|
||||
}
|
||||
}
|
||||
|
||||
+37
-79
@@ -15,6 +15,7 @@ protocol SeismicNetworkTableViewCellDelegate: AnyObject {
|
||||
func seismicNetworkCellDidTapShare(_ cell: SeismicNetworkTableViewCell)
|
||||
func seismicNetworkCellDidTapMap(_ cell: SeismicNetworkTableViewCell)
|
||||
func seismicNetworkCellDidTapMapDetail(_ cell: SeismicNetworkTableViewCell)
|
||||
func seismicNetworkCellDidTapIntensityMapDetail(_ cell: SeismicNetworkTableViewCell)
|
||||
func seismicNetworkCellDidTapCalendar(_ cell: SeismicNetworkTableViewCell)
|
||||
func seismicNetworkCellDidTapSettings(_ cell: SeismicNetworkTableViewCell)
|
||||
func seismicNetworkCellDidTapClose(_ cell: SeismicNetworkTableViewCell)
|
||||
@@ -23,9 +24,7 @@ protocol SeismicNetworkTableViewCellDelegate: AnyObject {
|
||||
class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
|
||||
static let Identifier = "SeismicNetworkTableViewCell"
|
||||
|
||||
typealias MagnitudeColors = (textColor: UIColor, startColor: UIColor, endColor: UIColor)
|
||||
|
||||
|
||||
/// Available informations to display inside the cell
|
||||
enum InformationType: Int {
|
||||
case preliminary
|
||||
@@ -35,6 +34,7 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
case population
|
||||
case realtimeSmartphones
|
||||
case reportUsers
|
||||
case intensityMap
|
||||
case buttons
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
private static let DefaultButtonHeight: CGFloat = 30.0
|
||||
private static let DefaultVerticalSpacing: CGFloat = 6.0
|
||||
|
||||
/// Seismic to show
|
||||
@@ -58,8 +59,6 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
private(set) var displayType = DisplayType.normal
|
||||
private var informationTypes = [InformationType]()
|
||||
private var isPushSelected = false
|
||||
|
||||
private var colors: MagnitudeColors?
|
||||
|
||||
// MARK: - UI Components
|
||||
|
||||
@@ -301,10 +300,17 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
stackViewInformations.topAnchor.constraint(equalTo: magnitudeLabel.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
|
||||
stackViewInformations.leadingAnchor.constraint(equalTo: informationsLeadingAnchor, constant: 14).isActive = true
|
||||
stackViewInformations.trailingAnchor.constraint(equalTo: informationsTrailingAnchor, constant: -14).isActive = true
|
||||
|
||||
previousView = stackViewInformations
|
||||
if informationTypes.contains(.realtimeSmartphones) || informationTypes.contains(.reportUsers) {
|
||||
let separator2 = addSeparator(constraintTo: stackViewInformations.bottomAnchor)
|
||||
|
||||
// network
|
||||
containerView.addSubview(networkLabel)
|
||||
networkLabel.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
|
||||
networkLabel.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
|
||||
networkLabel.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
|
||||
previousView = networkLabel
|
||||
|
||||
if informationTypes.contains(.realtimeSmartphones) || informationTypes.contains(.reportUsers) || informationTypes.contains(.intensityMap) {
|
||||
let separator2 = addSeparator(constraintTo: previousView.bottomAnchor)
|
||||
|
||||
let stackViewReports = UIStackView()
|
||||
stackViewReports.translatesAutoresizingMaskIntoConstraints = false
|
||||
@@ -319,30 +325,30 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
if informationTypes.contains(.reportUsers) {
|
||||
stackViewReports.addArrangedSubview(alertsLabel)
|
||||
}
|
||||
if informationTypes.contains(.intensityMap) {
|
||||
let buttonMap = EQNRoundedButton.make(title: "🎯 \(NSLocalizedString("shakemap", comment: ""))", target: self, action: #selector(intensityMapTapped(_:)))
|
||||
stackViewReports.addArrangedSubview(buttonMap)
|
||||
buttonMap.heightAnchor.constraint(equalToConstant: Self.DefaultButtonHeight).isActive = true
|
||||
buttonMap.leadingAnchor.constraint(equalTo: stackViewReports.leadingAnchor).isActive = true
|
||||
buttonMap.trailingAnchor.constraint(equalTo: stackViewReports.trailingAnchor).isActive = true
|
||||
}
|
||||
|
||||
containerView.addSubview(stackViewReports)
|
||||
stackViewReports.topAnchor.constraint(equalTo: separator2.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
|
||||
stackViewReports.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor, constant: 20.0).isActive = true
|
||||
stackViewReports.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor, constant: -20.0).isActive = true
|
||||
stackViewReports.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
|
||||
stackViewReports.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
|
||||
|
||||
let separator3 = addSeparator(constraintTo: stackViewReports.bottomAnchor)
|
||||
previousView = separator3
|
||||
}
|
||||
|
||||
// network
|
||||
containerView.addSubview(networkLabel)
|
||||
networkLabel.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
|
||||
networkLabel.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
|
||||
networkLabel.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
|
||||
previousView = networkLabel
|
||||
|
||||
|
||||
if informationTypes.contains(.buttons) {
|
||||
// buttons
|
||||
let stackViewButtons = UIStackView()
|
||||
stackViewButtons.translatesAutoresizingMaskIntoConstraints = false
|
||||
stackViewButtons.axis = .horizontal
|
||||
stackViewButtons.distribution = .fillEqually
|
||||
stackViewButtons.spacing = 4
|
||||
stackViewButtons.spacing = 8
|
||||
|
||||
let buttonMap = EQNRoundedButton.make(title: "🗺", target: self, action: #selector(mapTapped(_:)))
|
||||
stackViewButtons.addArrangedSubview(buttonMap)
|
||||
@@ -352,7 +358,7 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
stackViewButtons.addArrangedSubview(buttonSettings)
|
||||
|
||||
containerView.addSubview(stackViewButtons)
|
||||
stackViewButtons.heightAnchor.constraint(equalToConstant: 30.0).isActive = true
|
||||
stackViewButtons.heightAnchor.constraint(equalToConstant: Self.DefaultButtonHeight).isActive = true
|
||||
stackViewButtons.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
|
||||
stackViewButtons.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
|
||||
stackViewButtons.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
|
||||
@@ -374,6 +380,7 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
let buttonClose = EQNRoundedButton.make(title: NSLocalizedString("official_close", comment: "").uppercased(), target: self, action: #selector(closeTapped(_:)))
|
||||
|
||||
containerView.addSubview(buttonClose)
|
||||
buttonClose.heightAnchor.constraint(equalToConstant: Self.DefaultButtonHeight).isActive = true
|
||||
buttonClose.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
|
||||
buttonClose.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
|
||||
buttonClose.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
|
||||
@@ -398,17 +405,13 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
|
||||
let viewModel = SeismicNetworkViewModel(seismic: seismic)
|
||||
|
||||
if let colors = colors {
|
||||
gradientView.image = .gradient(from: colors.startColor, to: colors.endColor, with: .init(origin: .zero, size: .init(width: 500, height: 1)))
|
||||
} else {
|
||||
gradientView.image = nil
|
||||
}
|
||||
|
||||
gradientView.image = .gradient(from: viewModel.colors.startColor, to: viewModel.colors.endColor, with: .init(origin: .zero, size: .init(width: 500, height: 1)))
|
||||
|
||||
// update seismic data
|
||||
placeLabel.text = viewModel.place
|
||||
placeLabel.textColor = isPushSelected ? AppTheme.Colors.pureBlue : AppTheme.shared.cardTextColor
|
||||
networkLabel.text = String(format: NSLocalizedString("official_provider", comment: ""), viewModel.network)
|
||||
magnitudeLabel.textColor = colors?.textColor
|
||||
magnitudeLabel.textColor = viewModel.colors.textColor
|
||||
magnitudeLabel.text = viewModel.magnitude
|
||||
depthLabel.text = viewModel.depth
|
||||
timeLabel.text = "🕗 \(viewModel.time)"
|
||||
@@ -461,7 +464,6 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
isPushSelected: Bool
|
||||
) {
|
||||
self.seismic = seismic
|
||||
self.colors = calculateColors(for: seismic.magnitude.doubleValue)
|
||||
self.displayType = type
|
||||
self.informationTypes = informations
|
||||
self.isPushSelected = isPushSelected
|
||||
@@ -479,6 +481,9 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
if seismic.userNumber.intValue > 0 && !informations.contains(.reportUsers) {
|
||||
self.informationTypes += [.reportUsers]
|
||||
}
|
||||
if seismic.isoCode == "0" && informations.contains(.intensityMap) {
|
||||
self.informationTypes.removeAll { $0 == .intensityMap }
|
||||
}
|
||||
|
||||
recreateUI()
|
||||
updateUI()
|
||||
@@ -512,6 +517,10 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
delegate?.seismicNetworkCellDidTapMapDetail(self)
|
||||
}
|
||||
|
||||
@objc func intensityMapTapped(_ sender: Any) {
|
||||
delegate?.seismicNetworkCellDidTapIntensityMapDetail(self)
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
@discardableResult
|
||||
@@ -541,55 +550,4 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
}
|
||||
return zoom
|
||||
}
|
||||
|
||||
/// Calculate colors to use for text and background of the cell
|
||||
private func calculateColors(for magnitude: Double) -> MagnitudeColors {
|
||||
var textColor = UIColor.black
|
||||
|
||||
var r = 0, g = 0, b = 0
|
||||
if (magnitude < 2.0) {
|
||||
let fraction: Double = 1 - (magnitude - 0.0) / (2.0 - 0.0)
|
||||
r = Int(round(200.0 + (255.0 - 200.0) * fraction))
|
||||
g = Int(round(226.0 + (255.0 - 226.0) * fraction))
|
||||
b = Int(round(196.0 + (255.0 - 196.0) * fraction))
|
||||
textColor = UIColor(red: 12.0 / 255.0, green: 115.0 / 255.0, blue: 160.0 / 255.0, alpha: 1.0)
|
||||
}
|
||||
if (magnitude >= 2.0 && magnitude < 3.5) {
|
||||
let fraction: Double = 1 - (magnitude - 2) / (3.5 - 2)
|
||||
r = Int(round(136.0 + (200.0 - 136.0) * fraction))
|
||||
g = Int(round(175.0 + (226.0 - 175.0) * fraction))
|
||||
b = Int(round(131.0 + (196.0 - 131.0) * fraction))
|
||||
textColor = UIColor(red: 12.0 / 255.0, green: 160.0 / 255.0, blue: 35.0 / 255.0, alpha: 1.0)
|
||||
}
|
||||
if (magnitude >= 3.5 && magnitude < 4.5) {
|
||||
let fraction: Double = 1 - (magnitude - 3.5) / (4.5 - 3.5)
|
||||
r = 252
|
||||
g = Int(round(233.0 + (253.0 - 233.0) * fraction))
|
||||
b = Int(round(179.0 + (209.0 - 179.0) * fraction))
|
||||
textColor = UIColor(red: 244.0 / 255.0, green: 195.0 / 255.0, blue: 0.0 / 255.0, alpha: 1.0)
|
||||
}
|
||||
if (magnitude >= 4.5 && magnitude < 5.5) {
|
||||
let fraction: Double = 1 - (magnitude - 4.5) / (5.5 - 4.5)
|
||||
r = 252
|
||||
g = Int(round(159.0 + (197.0 - 159.0) * fraction))
|
||||
b = Int(round(161.0 + (197.0 - 161.0) * fraction))
|
||||
textColor = UIColor(red: 255.0 / 255.0, green: 0.0 / 255.0, blue: 0.0 / 255.0, alpha: 1.0)
|
||||
}
|
||||
if (magnitude >= 5.5) {
|
||||
let fraction: Double = 1 - (magnitude - 5.5) / (10 - 5.5)
|
||||
r = Int(round(190.0 + (254.0 - 190.0) * fraction))
|
||||
g = Int(round(124.0 + (219.0 - 124.0) * fraction))
|
||||
b = 255
|
||||
textColor = UIColor(red: 183.0 / 255.0, green: 60.0 / 255.0, blue: 252.0 / 255.0, alpha: 1.0)
|
||||
}
|
||||
|
||||
let r2 = min(r + 30, 255)
|
||||
let g2 = min(g + 30, 255)
|
||||
let b2 = min(b + 30, 255)
|
||||
|
||||
let startColor = UIColor(red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: 1.0)
|
||||
let endColor = UIColor(red: CGFloat(r2) / 255.0, green: CGFloat(g2) / 255.0, blue: CGFloat(b2) / 255.0, alpha: 1.0)
|
||||
|
||||
return (textColor: textColor, startColor: startColor, endColor: endColor)
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -21,6 +21,7 @@ class SeismicFiltersViewController: UIViewController, UITableViewDelegate, UITab
|
||||
case magnitudoMinima
|
||||
case sismiRilevanti
|
||||
case sismiTutti
|
||||
case sismiPercepiti
|
||||
}
|
||||
|
||||
weak var delegate: SeismicFiltersViewControllerDelegate?
|
||||
@@ -40,6 +41,7 @@ class SeismicFiltersViewController: UIViewController, UITableViewDelegate, UITab
|
||||
SettingItem(type: .slider, title: NSLocalizedString("filter_minimum_magnitude", comment: "")),
|
||||
SettingItem(type: .enable, title: NSLocalizedString("filter_show_relevant", comment: "")),
|
||||
SettingItem(type: .enable, title: NSLocalizedString("filter_show_all", comment: "")),
|
||||
SettingItem(type: .enable, title: NSLocalizedString("filter_show_felt", comment: ""))
|
||||
]
|
||||
|
||||
private let initialFilterType = EQNSeismic.shared.filterOption
|
||||
@@ -148,6 +150,12 @@ class SeismicFiltersViewController: UIViewController, UITableViewDelegate, UITab
|
||||
cell.valueChanged = { [weak self] enabled in
|
||||
self?.onChangeFilterOption(enabled, filter: .worldWide)
|
||||
}
|
||||
case .sismiPercepiti:
|
||||
let isCurrentFilter = currentFilterType == .userFelt
|
||||
cell.toggleSwitch.isOn = isCurrentFilter
|
||||
cell.valueChanged = { [weak self] enabled in
|
||||
self?.onChangeFilterOption(enabled, filter: .userFelt)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
//
|
||||
// SeismicNetworkData.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 31/01/25.
|
||||
// Copyright © 2025 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// SeismicNetworkScrollIndicatorView.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 31/01/25.
|
||||
// Copyright © 2025 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import CoreGraphics
|
||||
|
||||
|
||||
class SeismicNetworkScrollIndicatorView: UIView {
|
||||
|
||||
private static let HighlightColor: UIColor = .red
|
||||
|
||||
var seismics: [SeismicNetworkViewModel] = [] {
|
||||
didSet {
|
||||
setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
var highlighted: SeismicNetworkViewModel? {
|
||||
didSet {
|
||||
setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
private var numberOfRectangles: Int {
|
||||
seismics.count
|
||||
}
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
guard numberOfRectangles > 0 else { return }
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()
|
||||
let rectStandardWidth = rect.width
|
||||
let rectStandardHeight = rect.height / CGFloat(numberOfRectangles)
|
||||
let rectHighlightedMinHeight: CGFloat = 4
|
||||
|
||||
let smallRectangles = rectStandardHeight < 10
|
||||
let highlightIndex = seismics.firstIndex(where: { $0 == highlighted }) ?? 100_000
|
||||
|
||||
|
||||
seismics.enumerated().forEach { index, seismic in
|
||||
// Disegniamo un rettangolo per ogni sisma, quello evidenziato deve avere un contorno rosso.
|
||||
// Ci sono situazioni in cui ci sono molti sismi da mostrare, quindi in quel caso facciamo alcune modifiche:
|
||||
// - usiamo un'altezza minma per il sisma evidenziato
|
||||
// - per il sisma evidenziato, anche il contenuto è rosso (e non solo il bordo)
|
||||
// - negli altri sismi, non mostriamo il bordo
|
||||
|
||||
if highlightIndex == index {
|
||||
// Stiamo disegnando il sisma evidenziato.
|
||||
// Valutiamo se utilizzare l'altezza minima.
|
||||
let rectHeight = smallRectangles ? rectHighlightedMinHeight : rectStandardHeight
|
||||
let yPosition = CGFloat(index) * rectStandardHeight
|
||||
let rectangle = CGRect(x: 0, y: yPosition, width: rectStandardWidth, height: rectHeight)
|
||||
|
||||
let fillColor = smallRectangles ? Self.HighlightColor : seismic.colors.textColor.withAlphaComponent(0.3)
|
||||
context?.setFillColor(fillColor.cgColor)
|
||||
context?.fill(rectangle)
|
||||
|
||||
if !smallRectangles {
|
||||
// disegniamo il bordo solo se i rettangoli non sono piccoli
|
||||
let borderWidth: CGFloat = 2.0
|
||||
context?.setStrokeColor(Self.HighlightColor.cgColor)
|
||||
context?.setLineWidth(borderWidth) // Spessore del bordo
|
||||
context?.stroke(rectangle.insetBy(dx: borderWidth / 2, dy: borderWidth / 2)) // Evita che il bordo venga tagliato
|
||||
}
|
||||
} else {
|
||||
// Stiamo disegnando i sismi non evidenziati, utilizziamo sempre l'altezza predefinita
|
||||
// Dobbiamo eventualmente calcolare un offset aggiuntivo,
|
||||
// perchè il sisma evidenziato ha un'altezza maggiore (se i rettangoli sono piccoli)
|
||||
let rectHeight = rectStandardHeight
|
||||
let offset: CGFloat = (index > highlightIndex && smallRectangles) ? rectHighlightedMinHeight : 0
|
||||
let yPosition = CGFloat(index) * rectHeight + offset
|
||||
let rectangle = CGRect(x: 0, y: yPosition, width: rectStandardWidth, height: rectHeight)
|
||||
|
||||
let fillColor = seismic.colors.textColor.withAlphaComponent(0.3)
|
||||
context?.setFillColor(fillColor.cgColor)
|
||||
context?.fill(rectangle)
|
||||
|
||||
if !smallRectangles {
|
||||
// altrimenti un bordo grigio
|
||||
let borderWidth: CGFloat = 0.5
|
||||
context?.setStrokeColor(AppTheme.Colors.gray.cgColor)
|
||||
context?.setLineWidth(borderWidth) // Spessore del bordo
|
||||
context?.stroke(rectangle) // Evita che il bordo venga tagliato
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,13 @@ import Foundation
|
||||
|
||||
struct SeismicNetworkViewModel {
|
||||
|
||||
struct MagnitudeColors {
|
||||
let textColor: UIColor
|
||||
let startColor: UIColor
|
||||
let endColor: UIColor
|
||||
}
|
||||
|
||||
private let seismic: EQNSisma
|
||||
let place: String
|
||||
let network: String
|
||||
let isPreliminary: Bool
|
||||
@@ -23,10 +30,12 @@ struct SeismicNetworkViewModel {
|
||||
let population: String
|
||||
let smartphones: String
|
||||
let users: String
|
||||
let colors: MagnitudeColors
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
init(seismic: EQNSisma) {
|
||||
self.seismic = seismic
|
||||
self.place = seismic.place
|
||||
self.network = seismic.provider
|
||||
self.rawMagnitude = seismic.magnitude.doubleValue
|
||||
@@ -71,6 +80,8 @@ struct SeismicNetworkViewModel {
|
||||
} else {
|
||||
self.users = ""
|
||||
}
|
||||
|
||||
self.colors = Self.calculateColors(for: seismic.magnitude.doubleValue)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
@@ -90,4 +101,62 @@ struct SeismicNetworkViewModel {
|
||||
}
|
||||
return populationString
|
||||
}
|
||||
|
||||
/// Calculate colors to use for text and background of the cell
|
||||
private static func calculateColors(for magnitude: Double) -> MagnitudeColors {
|
||||
var textColor = UIColor.black
|
||||
|
||||
var r = 0, g = 0, b = 0
|
||||
if (magnitude < 2.0) {
|
||||
let fraction: Double = 1 - (magnitude - 0.0) / (2.0 - 0.0)
|
||||
r = Int(round(200.0 + (255.0 - 200.0) * fraction))
|
||||
g = Int(round(226.0 + (255.0 - 226.0) * fraction))
|
||||
b = Int(round(196.0 + (255.0 - 196.0) * fraction))
|
||||
textColor = UIColor(red: 12.0 / 255.0, green: 115.0 / 255.0, blue: 160.0 / 255.0, alpha: 1.0)
|
||||
}
|
||||
if (magnitude >= 2.0 && magnitude < 3.5) {
|
||||
let fraction: Double = 1 - (magnitude - 2) / (3.5 - 2)
|
||||
r = Int(round(136.0 + (200.0 - 136.0) * fraction))
|
||||
g = Int(round(175.0 + (226.0 - 175.0) * fraction))
|
||||
b = Int(round(131.0 + (196.0 - 131.0) * fraction))
|
||||
textColor = UIColor(red: 12.0 / 255.0, green: 160.0 / 255.0, blue: 35.0 / 255.0, alpha: 1.0)
|
||||
}
|
||||
if (magnitude >= 3.5 && magnitude < 4.5) {
|
||||
let fraction: Double = 1 - (magnitude - 3.5) / (4.5 - 3.5)
|
||||
r = 252
|
||||
g = Int(round(233.0 + (253.0 - 233.0) * fraction))
|
||||
b = Int(round(179.0 + (209.0 - 179.0) * fraction))
|
||||
textColor = UIColor(red: 244.0 / 255.0, green: 195.0 / 255.0, blue: 0.0 / 255.0, alpha: 1.0)
|
||||
}
|
||||
if (magnitude >= 4.5 && magnitude < 5.5) {
|
||||
let fraction: Double = 1 - (magnitude - 4.5) / (5.5 - 4.5)
|
||||
r = 252
|
||||
g = Int(round(159.0 + (197.0 - 159.0) * fraction))
|
||||
b = Int(round(161.0 + (197.0 - 161.0) * fraction))
|
||||
textColor = UIColor(red: 255.0 / 255.0, green: 0.0 / 255.0, blue: 0.0 / 255.0, alpha: 1.0)
|
||||
}
|
||||
if (magnitude >= 5.5) {
|
||||
let fraction: Double = 1 - (magnitude - 5.5) / (10 - 5.5)
|
||||
r = Int(round(190.0 + (254.0 - 190.0) * fraction))
|
||||
g = Int(round(124.0 + (219.0 - 124.0) * fraction))
|
||||
b = 255
|
||||
textColor = UIColor(red: 183.0 / 255.0, green: 60.0 / 255.0, blue: 252.0 / 255.0, alpha: 1.0)
|
||||
}
|
||||
|
||||
let r2 = min(r + 30, 255)
|
||||
let g2 = min(g + 30, 255)
|
||||
let b2 = min(b + 30, 255)
|
||||
|
||||
let startColor = UIColor(red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: 1.0)
|
||||
let endColor = UIColor(red: CGFloat(r2) / 255.0, green: CGFloat(g2) / 255.0, blue: CGFloat(b2) / 255.0, alpha: 1.0)
|
||||
|
||||
return .init(textColor: textColor, startColor: startColor, endColor: endColor)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension SeismicNetworkViewModel: Equatable {
|
||||
static func == (lhs: SeismicNetworkViewModel, rhs: SeismicNetworkViewModel) -> Bool {
|
||||
return lhs.seismic == rhs.seismic
|
||||
}
|
||||
}
|
||||
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
//
|
||||
// SeismicNetworksIntensityMapViewController.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 27/02/25.
|
||||
// Copyright © 2025 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import MapKit
|
||||
|
||||
class SeismicNetworksIntensityMapViewController: EQNBaseMapViewController {
|
||||
|
||||
private let seismic: EQNSisma
|
||||
private var shakemaps: [EQNShakemap] = []
|
||||
private var pinStyle: MapPinStyle {
|
||||
get { AppPreferences.shared.mapPinStyle }
|
||||
set { AppPreferences.shared.mapPinStyle = newValue }
|
||||
}
|
||||
|
||||
override var isFilterViewVisible: Bool { false }
|
||||
override var isCloseButtonVisible: Bool { false }
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
init(
|
||||
seismic: EQNSisma
|
||||
) {
|
||||
self.seismic = seismic
|
||||
super.init()
|
||||
}
|
||||
|
||||
@MainActor required init?(coder: NSCoder) {
|
||||
fatalError("Plase use init(seismic:) instead")
|
||||
}
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
|
||||
override func extraUI() {
|
||||
super.extraUI()
|
||||
|
||||
let descriptionView = UIView()
|
||||
descriptionView.translatesAutoresizingMaskIntoConstraints = false
|
||||
descriptionView.backgroundColor = AppTheme.Colors.pureBlue
|
||||
view.addSubview(descriptionView)
|
||||
descriptionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
|
||||
descriptionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
|
||||
descriptionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
|
||||
|
||||
let descriptionLabel = UILabel()
|
||||
descriptionLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
descriptionLabel.numberOfLines = 0
|
||||
descriptionLabel.textColor = .white
|
||||
descriptionLabel.font = .preferredFont(forTextStyle: .subheadline)
|
||||
descriptionLabel.textAlignment = .center
|
||||
descriptionLabel.text = NSLocalizedString("shakemap_description", comment: "")
|
||||
|
||||
descriptionView.addSubview(descriptionLabel)
|
||||
descriptionLabel.leadingAnchor.constraint(equalTo: descriptionView.leadingAnchor, constant: 2.0).isActive = true
|
||||
descriptionLabel.trailingAnchor.constraint(equalTo: descriptionView.trailingAnchor, constant: -2.0).isActive = true
|
||||
descriptionLabel.topAnchor.constraint(equalTo: descriptionView.topAnchor, constant: 2.0).isActive = true
|
||||
descriptionLabel.bottomAnchor.constraint(equalTo: descriptionView.bottomAnchor, constant: -2.0).isActive = true
|
||||
}
|
||||
|
||||
override func configureUI() {
|
||||
super.configureUI()
|
||||
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(systemItem: .done, primaryAction: .init(handler: { [weak self] _ in
|
||||
self?.dismiss(animated: true)
|
||||
}))
|
||||
navigationItem.rightBarButtonItems = [
|
||||
UIBarButtonItem(image: UIImage(named: "navbar-icon-screenshot"), primaryAction: .init(handler: { [weak self] _ in
|
||||
self?.shareScreenshot()
|
||||
})),
|
||||
UIBarButtonItem(image: UIImage(named: "navbar-icon-pin-arrow"), primaryAction: .init(handler: { [weak self] _ in
|
||||
self?.nextPinStyle()
|
||||
})),
|
||||
]
|
||||
}
|
||||
|
||||
override func registerMapAnnotationViews() {
|
||||
mapView.register(EQNSeismicAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNSeismicAnnotationView.identifier(for: .full))
|
||||
mapView.register(EQNSeismicAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNSeismicAnnotationView.identifier(for: .light))
|
||||
mapView.register(EQNSeismicAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNSeismicAnnotationView.identifier(for: .circle))
|
||||
}
|
||||
|
||||
override func loadDataSource() {
|
||||
Task {
|
||||
let result = try await APIService.shared.fetchShakemap(isoCode: seismic.isoCode)
|
||||
elaborateShakemaps(result)
|
||||
}
|
||||
}
|
||||
|
||||
override func elaborateMapCenter() {
|
||||
setMapCenter(for: seismic.coordinate, span: MKCoordinateSpan(latitudeDelta: 2, longitudeDelta: 2))
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func elaborateShakemaps(_ shakemaps: [EQNShakemap]) {
|
||||
self.shakemaps = shakemaps
|
||||
|
||||
var shakemapPolyline = [MKPolyline]()
|
||||
var shakemapAnnotations: [MKAnnotation] = []
|
||||
for shakemap in shakemaps {
|
||||
// create coordinates for current shakemap
|
||||
let coordinates = zip(shakemap.lat, shakemap.lon).map { lat, lon in
|
||||
CLLocationCoordinate2D(latitude: Double(lat) / 10_000.0, longitude: Double(lon) / 10_000.0)
|
||||
}
|
||||
|
||||
let intensityColors = getColors(for: shakemap.intensity)
|
||||
|
||||
// create line to show on map
|
||||
let polyline = ShakemapPolyline(coordinates: coordinates, count: coordinates.count)
|
||||
polyline.intensity = shakemap.intensity
|
||||
polyline.intensityColor = intensityColors.lineColor
|
||||
shakemapPolyline.append(polyline)
|
||||
|
||||
// create annotation to show on top of the line
|
||||
let middlePoint = coordinates[coordinates.count / 2]
|
||||
let annotation = EQNMapAnnotationShakemap(coordinate: middlePoint, shakemap: shakemap)
|
||||
annotation.intensityColor = intensityColors.lineColor
|
||||
annotation.intensityTextColor = intensityColors.textColor
|
||||
shakemapAnnotations.append(annotation)
|
||||
}
|
||||
|
||||
let seismicAnnotation = EQNMapAnnotationSeismic(seismic: seismic)
|
||||
shakemapAnnotations.append(seismicAnnotation)
|
||||
|
||||
// draw lines
|
||||
mapView.addOverlays(shakemapPolyline)
|
||||
updateMap(with: shakemapAnnotations)
|
||||
}
|
||||
|
||||
private func nextPinStyle() {
|
||||
pinStyle.next()
|
||||
reloadMap()
|
||||
}
|
||||
|
||||
private func shareScreenshot() {
|
||||
let screenshot = createSnapshot()
|
||||
|
||||
let controller = UIActivityViewController(activityItems: [screenshot], applicationActivities: [])
|
||||
present(controller, animated: true)
|
||||
}
|
||||
|
||||
// MARK: - MKMapViewDelegate
|
||||
|
||||
override func setupAnnotationView(for annotation: MKAnnotation, on mapView: MKMapView) -> MKAnnotationView? {
|
||||
switch annotation {
|
||||
case let shakemapAnnotation as EQNMapAnnotationShakemap:
|
||||
return shakemapAnnotation.toAnnotationView(mapView: mapView, style: .light)
|
||||
case let seismicAnnotation as EQNMapAnnotationSeismic:
|
||||
return seismicAnnotation.toAnnotationView(mapView: mapView, style: pinStyle)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
|
||||
if let polyline = overlay as? ShakemapPolyline {
|
||||
let renderer = MKPolylineRenderer(polyline: polyline)
|
||||
renderer.strokeColor = polyline.intensityColor
|
||||
renderer.lineWidth = 6.0
|
||||
return renderer
|
||||
}
|
||||
return MKOverlayRenderer()
|
||||
}
|
||||
|
||||
private func getColors(for intensity: Float) -> (textColor: UIColor, lineColor: UIColor) {
|
||||
let shakemapColors: [String] = [
|
||||
"#3E26A8","#3E27AC","#3F28AF","#3F29B2","#402AB4","#402BB7","#412CBA","#412DBD","#422EBF","#422FC2",
|
||||
"#4330C5","#4331C8","#4332CA","#4433CD","#4434D0","#4535D2","#4537D5","#4538D7","#4639D9","#463ADC",
|
||||
"#463BDE","#463DE0","#473EE1","#473FE3","#4741E5","#4742E6","#4744E8","#4745E9","#4746EB","#4848EC",
|
||||
"#4849ED","#484BEE","#484CF0","#484EF1","#484FF2","#4850F3","#4852F4","#4853F5","#4854F6","#4756F7",
|
||||
"#4757F7","#4759F8","#475AF9","#475BFA","#475DFA","#465EFB","#4660FB","#4661FC","#4562FC","#4564FD",
|
||||
"#4465FD","#4367FD","#4368FE","#426AFE","#416BFE","#406DFE","#3F6EFF","#3E70FF","#3C71FF","#3B73FF",
|
||||
"#3974FF","#3876FE","#3677FE","#3579FD","#337AFD","#327CFC","#317DFC","#307FFB","#2F80FA","#2F82FA",
|
||||
"#2E83F9","#2E84F8","#2E86F8","#2E87F7","#2D88F6","#2D8AF5","#2D8BF4","#2D8CF3","#2D8EF2","#2C8FF1",
|
||||
"#2C90F0","#2B91EF","#2A93EE","#2994ED","#2895EC","#2797EB","#2798EA","#2699E9","#269AE8","#259BE8",
|
||||
"#259CE7","#249EE6","#249FE5","#23A0E5","#23A1E4","#22A2E4","#21A3E3","#20A5E3","#1FA6E2","#1EA7E1",
|
||||
"#1DA8E1","#1DA9E0","#1CAADF","#1BABDE","#1AACDD","#19ADDC","#17AEDA","#16AFD9","#14B0D8","#12B1D6",
|
||||
"#10B2D5","#0EB3D4","#0BB3D2","#08B4D1","#06B5CF","#04B6CE","#02B7CC","#01B7CA","#00B8C9","#00B9C7",
|
||||
"#00BAC6","#01BAC4","#02BBC2","#04BBC1","#06BCBF","#09BDBD","#0DBDBC","#10BEBA","#14BEB8","#17BFB6",
|
||||
"#1AC0B5","#1DC0B3","#20C1B1","#23C1AF","#25C2AE","#27C2AC","#29C3AA","#2BC3A8","#2CC4A6","#2EC4A5",
|
||||
"#2FC5A3","#31C5A1","#32C69F","#33C79D","#35C79B","#36C899","#38C896","#39C994","#3BC992","#3DCA90",
|
||||
"#40CA8D","#42CA8B","#45CB89","#48CB86","#4BCB84","#4ECC81","#51CC7F","#54CC7C","#57CC7A","#5ACC77",
|
||||
"#5ECD74","#61CD72","#64CD6F","#67CD6C","#6BCD69","#6ECD66","#72CD64","#76CC61","#79CC5E","#7DCC5B",
|
||||
"#81CC59","#84CC56","#88CB53","#8BCB51","#8FCB4E","#93CA4B","#96CA48","#9AC946","#9DC943","#A1C840",
|
||||
"#A4C83E","#A7C73B","#ABC739","#AEC637","#B2C635","#B5C533","#B8C431","#BBC42F","#BEC32D","#C2C32C",
|
||||
"#C5C22A","#C8C129","#CBC128","#CEC027","#D0BF27","#D3BF27","#D6BE27","#D9BE28","#DBBD28","#DEBC29",
|
||||
"#E1BC2A","#E3BC2B","#E6BB2D","#E8BB2E","#EABA30","#ECBA32","#EFBA35","#F1BA37","#F3BA39","#F5BA3B",
|
||||
"#F7BA3D","#F9BA3E","#FBBB3E","#FCBC3E","#FEBD3D","#FEBE3C","#FEC03B","#FEC13A","#FEC239","#FEC438",
|
||||
"#FEC537","#FEC735","#FEC834","#FECA33","#FDCB32","#FDCD31","#FDCE31","#FCD030","#FBD22F","#FBD32E",
|
||||
"#FAD52E","#F9D62D","#F9D82C","#F8D92B","#F7DB2A","#F7DD2A","#F6DE29","#F6E028","#F5E128","#F5E327",
|
||||
"#F5E526","#F5E626","#F5E825","#F5E924","#F5EB23","#F5EC22","#F5EE21","#F6EF20","#F6F11F","#F6F21E",
|
||||
"#F7F41C","#F7F51B","#F8F71A","#F8F818","#F9F916","#F9FB15"
|
||||
]
|
||||
|
||||
let minIntensity = shakemaps.map { $0.intensity }.min() ?? 0
|
||||
let maxIntensity = shakemaps.map { $0.intensity }.max() ?? 255
|
||||
let indexColor = Int(round((intensity-minIntensity)/(maxIntensity-minIntensity)*255))
|
||||
|
||||
let lineColor = UIColor(hexString: shakemapColors[indexColor]) ?? .white
|
||||
let textColor: UIColor = indexColor < 65 ? .white : .black
|
||||
|
||||
return (textColor: textColor, lineColor: lineColor)
|
||||
}
|
||||
}
|
||||
|
||||
extension EQNMapAnnotationShakemap {
|
||||
func toAnnotationView(
|
||||
mapView: MKMapView,
|
||||
style: MapPinStyle,
|
||||
isUserSelection: Bool = false
|
||||
) -> MKAnnotationView? {
|
||||
switch style {
|
||||
case .full, .light:
|
||||
let identifier = EQNSeismicAnnotationView.identifier(for: style)
|
||||
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier, for: self) as! EQNSeismicAnnotationView
|
||||
annotationView.magnitude = String(format: "%.1f", shakemap.intensity)
|
||||
annotationView.magnitudeTextColor = intensityTextColor ?? .black
|
||||
annotationView.magnitudeBackgroundColor = intensityColor
|
||||
annotationView.canShowCallout = true
|
||||
return annotationView
|
||||
case .circle:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class ShakemapPolyline: MKPolyline {
|
||||
var intensity: Float = 0
|
||||
var intensityColor: UIColor = .white
|
||||
}
|
||||
+37
-34
@@ -15,21 +15,11 @@ protocol SeismicNetworksMapDetailViewControllerDelegate: AnyObject {
|
||||
}
|
||||
|
||||
class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
|
||||
|
||||
private enum PinStyle: CaseIterable {
|
||||
case full
|
||||
case light
|
||||
case circle
|
||||
|
||||
func next() -> Self {
|
||||
let all = Self.allCases
|
||||
let idx = all.firstIndex(of: self)!
|
||||
let next = all.index(after: idx)
|
||||
return all[next == all.endIndex ? all.startIndex : next]
|
||||
}
|
||||
}
|
||||
|
||||
private var pinStyle: PinStyle = .full
|
||||
private var pinStyle: MapPinStyle {
|
||||
get { AppPreferences.shared.mapPinStyle }
|
||||
set { AppPreferences.shared.mapPinStyle = newValue }
|
||||
}
|
||||
private let eqnSeismic = EQNSeismic.shared
|
||||
|
||||
// MARK: - State
|
||||
@@ -116,9 +106,9 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
|
||||
}
|
||||
|
||||
override func registerMapAnnotationViews() {
|
||||
mapView.register(EQNSeismicAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNSeismicAnnotationView.IdentifierFull)
|
||||
mapView.register(EQNSeismicAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNSeismicAnnotationView.IdentifierLight)
|
||||
mapView.register(EQNSeismicAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNSeismicAnnotationView.IdentifierCircle)
|
||||
mapView.register(EQNSeismicAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNSeismicAnnotationView.identifier(for: .full))
|
||||
mapView.register(EQNSeismicAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNSeismicAnnotationView.identifier(for: .light))
|
||||
mapView.register(EQNSeismicAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNSeismicAnnotationView.identifier(for: .circle))
|
||||
}
|
||||
|
||||
override func loadDataSource() {
|
||||
@@ -188,7 +178,7 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
|
||||
// MARK: - Private
|
||||
|
||||
private func nextPinStyle() {
|
||||
pinStyle = pinStyle.next()
|
||||
pinStyle.next()
|
||||
reloadMap()
|
||||
}
|
||||
|
||||
@@ -199,6 +189,7 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
|
||||
case .inRadius: NSLocalizedString("filter_area", comment: "")
|
||||
case .positionRelevant: NSLocalizedString("filter_relevant", comment: "")
|
||||
case .worldWide: NSLocalizedString("filter_all", comment: "")
|
||||
case .userFelt: NSLocalizedString("filter_felt", comment: "")
|
||||
}
|
||||
seismicsFilterLabel.text = text
|
||||
}
|
||||
@@ -240,22 +231,7 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
|
||||
}
|
||||
|
||||
let isUserSelection = annotation.seismic == seismic
|
||||
switch pinStyle {
|
||||
case .full, .light:
|
||||
let identifier = pinStyle == .full ? EQNSeismicAnnotationView.IdentifierFull : EQNSeismicAnnotationView.IdentifierLight
|
||||
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier, for: annotation) as! EQNSeismicAnnotationView
|
||||
annotationView.title = annotation.title
|
||||
annotationView.subtitle = annotation.subtitle
|
||||
annotationView.magnitude = String(format: "M%.1f", annotation.seismic.magnitude.doubleValue)
|
||||
annotationView.magnitudeColor = annotation.textColor
|
||||
annotationView.isUserSelection = isUserSelection
|
||||
return annotationView
|
||||
case .circle:
|
||||
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: EQNSeismicAnnotationView.IdentifierCircle, for: annotation) as! EQNSeismicAnnotationView
|
||||
annotationView.image = annotation.image(height: EQNSeismicAnnotationView.CircleViewHeight,
|
||||
isUserSelection: isUserSelection)
|
||||
return annotationView
|
||||
}
|
||||
return annotation.toAnnotationView(mapView: mapView, style: pinStyle, isUserSelection: isUserSelection)
|
||||
}
|
||||
|
||||
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
|
||||
@@ -270,6 +246,33 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
|
||||
}
|
||||
}
|
||||
|
||||
extension EQNMapAnnotationSeismic {
|
||||
func toAnnotationView(
|
||||
mapView: MKMapView,
|
||||
style: MapPinStyle,
|
||||
isUserSelection: Bool = false
|
||||
) -> MKAnnotationView {
|
||||
switch style {
|
||||
case .full, .light:
|
||||
let identifier = EQNSeismicAnnotationView.identifier(for: style)
|
||||
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier, for: self) as! EQNSeismicAnnotationView
|
||||
annotationView.title = self.title
|
||||
annotationView.subtitle = self.subtitle
|
||||
annotationView.magnitude = String(format: "M%.1f", self.seismic.magnitude.doubleValue)
|
||||
annotationView.magnitudeTextColor = self.textColor
|
||||
annotationView.magnitudeBackgroundColor = .white
|
||||
annotationView.isUserSelection = isUserSelection
|
||||
return annotationView
|
||||
case .circle:
|
||||
let identifier = EQNSeismicAnnotationView.identifier(for: style)
|
||||
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier, for: self) as! EQNSeismicAnnotationView
|
||||
annotationView.image = image(height: EQNSeismicAnnotationView.CircleViewHeight,
|
||||
isUserSelection: isUserSelection)
|
||||
return annotationView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SeismicNetworksMapDetailViewController: SeismicFiltersViewControllerDelegate {
|
||||
func seismicFiltersControllerDidUpdateFilters(_ controller: SeismicFiltersViewController) {
|
||||
delegate?.seismicNetworksMapDetailControllerWillUpdateData(self, needsDataUpdate: controller.needsDataUpdate)
|
||||
|
||||
+84
-12
@@ -15,7 +15,7 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
|
||||
private enum CellType {
|
||||
case seismic(EQNSisma)
|
||||
case advertise(GADNativeAd)
|
||||
case advertise(NativeAd)
|
||||
}
|
||||
|
||||
private static let SegueIdentifierFilters = "ShowFilters"
|
||||
@@ -24,8 +24,8 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
// MARK: - Internal
|
||||
|
||||
/// The ad loader
|
||||
private lazy var adLoader: GADAdLoader = {
|
||||
let adLoader = GADAdLoader(
|
||||
private lazy var adLoader: AdLoader = {
|
||||
let adLoader = AdLoader(
|
||||
adUnitID: EQNAdMobAppIdNativeBanner, rootViewController: self,
|
||||
adTypes: [.native], options: nil)
|
||||
adLoader.delegate = self
|
||||
@@ -34,6 +34,7 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
|
||||
/// Cells to display (must be seismics or ad banners)
|
||||
private var rows = [CellType]()
|
||||
private var seismicViewModels = [SeismicNetworkViewModel]()
|
||||
/// Informations to display on a single cell
|
||||
private var informations = [SeismicNetworkTableViewCell.InformationType]()
|
||||
/// Index path of row with map expanded
|
||||
@@ -47,6 +48,8 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
private var scrollToOpenedSeismic = false
|
||||
/// Current displayed controller with map
|
||||
private weak var currentMapController: SeismicNetworksMapDetailViewController?
|
||||
/// Keep track of the current cell at the center
|
||||
private var currentCenteredIndexPath: IndexPath?
|
||||
|
||||
// MARK: - UI
|
||||
|
||||
@@ -59,6 +62,7 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
tableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
tableView.showsVerticalScrollIndicator = false
|
||||
return tableView
|
||||
}()
|
||||
|
||||
@@ -85,6 +89,14 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
label.textAlignment = .center
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var scrollIndicatorView: SeismicNetworkScrollIndicatorView = {
|
||||
let view = SeismicNetworkScrollIndicatorView(frame: .zero)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.backgroundColor = .clear
|
||||
view.layer.borderColor = AppTheme.Colors.gray.cgColor
|
||||
return view
|
||||
}()
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
|
||||
@@ -121,13 +133,19 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
// MARK: - Private
|
||||
|
||||
private func setupUI() {
|
||||
view.addSubview(scrollIndicatorView)
|
||||
view.addSubview(tableView)
|
||||
|
||||
scrollIndicatorView.topAnchor.constraint(equalTo: tableView.topAnchor).isActive = true
|
||||
scrollIndicatorView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
|
||||
scrollIndicatorView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
|
||||
scrollIndicatorView.widthAnchor.constraint(equalToConstant: 10.0).isActive = true
|
||||
|
||||
tableViewTopConstraint = tableView.topAnchor.constraint(equalTo: view.topAnchor)
|
||||
tableViewTopConstraint?.isActive = true
|
||||
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
|
||||
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
|
||||
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
|
||||
tableView.trailingAnchor.constraint(equalTo: scrollIndicatorView.leadingAnchor).isActive = true
|
||||
tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
|
||||
}
|
||||
|
||||
private func setupFilterView(isVisible: Bool) {
|
||||
@@ -220,6 +238,12 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
self.currentMapController = controller
|
||||
}
|
||||
|
||||
private func showIntensityMap(for seismic: EQNSisma) {
|
||||
let controller = SeismicNetworksIntensityMapViewController(seismic: seismic)
|
||||
let navController = UINavigationController(rootViewController: controller)
|
||||
present(navController, animated: true)
|
||||
}
|
||||
|
||||
// MARK: - Notifications
|
||||
|
||||
@objc func didReceiveDownloadCompleteNotification(_ sender: Notification) {
|
||||
@@ -246,6 +270,7 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
}
|
||||
|
||||
tableView.reloadData()
|
||||
updateCenterCellIndexPath()
|
||||
|
||||
if scrollToOpenedSeismic, let index = getSeismics().firstIndex(where: { isSeismicToHighlight(seismic: $0) }) {
|
||||
tableView.scrollToRow(at: IndexPath(row: index, section: 0), at: .middle, animated: true)
|
||||
@@ -254,7 +279,7 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
}
|
||||
|
||||
private func loadAd() {
|
||||
adLoader.load(GADRequest())
|
||||
adLoader.load(Request())
|
||||
}
|
||||
|
||||
private func loadData(forced: Bool) {
|
||||
@@ -266,7 +291,8 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
let allSeismics = EQNManager.manager().retiSismiche
|
||||
let filteredSeismics = EQNSeismic.shared.filterSeismicList(allSeismics ?? [])
|
||||
rows = filteredSeismics.map { .seismic($0) }
|
||||
|
||||
seismicViewModels = filteredSeismics.map(SeismicNetworkViewModel.init)
|
||||
|
||||
#if ADS_ENABLED
|
||||
// if is not a pro user, show an advertise
|
||||
if !EQNPurchaseUtility.isProVersionEnabled() {
|
||||
@@ -278,6 +304,9 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
if let mapController = currentMapController {
|
||||
mapController.updateSeismics(filteredSeismics)
|
||||
}
|
||||
|
||||
scrollIndicatorView.seismics = seismicViewModels
|
||||
currentCenteredIndexPath = nil
|
||||
}
|
||||
|
||||
private func getSeismics() -> [EQNSisma] {
|
||||
@@ -560,6 +589,37 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
return false
|
||||
}
|
||||
|
||||
private func getCenterCellIndexPath() -> IndexPath? {
|
||||
let centerPoint = CGPoint(x: tableView.bounds.midX, y: tableView.bounds.midY)
|
||||
if let indexPath = tableView.indexPathForRow(at: centerPoint) {
|
||||
return indexPath
|
||||
}
|
||||
|
||||
// Se il metodo diretto fallisce, cerchiamo la cella più vicina
|
||||
if let visibleIndexPaths = tableView.indexPathsForVisibleRows {
|
||||
return visibleIndexPaths.min(by: { (indexPath1, indexPath2) -> Bool in
|
||||
let rect1 = tableView.rectForRow(at: indexPath1)
|
||||
let rect2 = tableView.rectForRow(at: indexPath2)
|
||||
let distance1 = abs(rect1.midY - centerPoint.y)
|
||||
let distance2 = abs(rect2.midY - centerPoint.y)
|
||||
return distance1 < distance2
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private func updateCenterCellIndexPath() {
|
||||
if let centerIndexPath = getCenterCellIndexPath(), centerIndexPath != currentCenteredIndexPath {
|
||||
currentCenteredIndexPath = centerIndexPath
|
||||
|
||||
let row = rows[centerIndexPath.row]
|
||||
if case .seismic = row, seismicViewModels.count > centerIndexPath.row {
|
||||
scrollIndicatorView.highlighted = seismicViewModels[centerIndexPath.row]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction func refreshDataTapped(_ sender: Any) {
|
||||
@@ -618,6 +678,12 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIScrollViewDelegate
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
updateCenterCellIndexPath()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func openCalendar(for seismic: EQNSisma) {
|
||||
@@ -673,18 +739,18 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
}
|
||||
}
|
||||
|
||||
extension SeismicNetworksViewController: GADNativeAdLoaderDelegate {
|
||||
func adLoader(_ adLoader: GADAdLoader, didReceive nativeAd: GADNativeAd) {
|
||||
print("[GADAdLoader] didReceive")
|
||||
extension SeismicNetworksViewController: NativeAdLoaderDelegate {
|
||||
func adLoader(_ adLoader: AdLoader, didReceive nativeAd: NativeAd) {
|
||||
print("[AdLoader] didReceive")
|
||||
|
||||
let adPosition = min(3, rows.count)
|
||||
rows.insert(.advertise(nativeAd), at: adPosition)
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
func adLoader(_ adLoader: GADAdLoader, didFailToReceiveAdWithError error: Error) {
|
||||
func adLoader(_ adLoader: AdLoader, didFailToReceiveAdWithError error: Error) {
|
||||
// nope
|
||||
print("[GADAdLoader] didFailToReceiveAdWithError: \(error.localizedDescription)")
|
||||
print("[AdLoader] didFailToReceiveAdWithError: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,6 +788,12 @@ extension SeismicNetworksViewController: SeismicNetworkTableViewCellDelegate {
|
||||
showMapDetail(for: seismic)
|
||||
}
|
||||
|
||||
func seismicNetworkCellDidTapIntensityMapDetail(_ cell: SeismicNetworkTableViewCell) {
|
||||
guard let index = tableView.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
|
||||
|
||||
showIntensityMap(for: seismic)
|
||||
}
|
||||
|
||||
func seismicNetworkCellDidTapCalendar(_ cell: SeismicNetworkTableViewCell) {
|
||||
guard let index = tableView.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// Log.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 27/02/25.
|
||||
// Copyright © 2025 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
|
||||
/// Use this protocol to have a base TAG in a Swift class
|
||||
public protocol Loggable {
|
||||
static var TAG: String { get }
|
||||
}
|
||||
|
||||
public extension Loggable {
|
||||
static var TAG: String {
|
||||
String(describing: Self.self)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController: Loggable { }
|
||||
|
||||
|
||||
public class Log {
|
||||
|
||||
private static let dumpDateFormatter: DateFormatter = {
|
||||
// create the default date formatter using ISO8601 date format
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
||||
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
|
||||
return formatter
|
||||
}()
|
||||
|
||||
private static let shared = Log()
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private let maxNumberOfLogsInDump: Int
|
||||
private let logsLifespanMillis: Int
|
||||
/// Subsystem for OSLog
|
||||
private let subsystem: String
|
||||
/// Logging in everything in a single "APP" category
|
||||
private let appCategory: String = "APP"
|
||||
|
||||
private lazy var logger: os.Logger = {
|
||||
os.Logger(subsystem: subsystem, category: appCategory)
|
||||
}()
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
@objc
|
||||
public init(
|
||||
subsystem: String = Bundle.main.bundleIdentifier!,
|
||||
maxNumberOfLogsInDump: Int = 5000,
|
||||
logsLifespanMillis: Int = 3 * 24 * 3600 * 1000
|
||||
) {
|
||||
self.subsystem = subsystem
|
||||
self.maxNumberOfLogsInDump = maxNumberOfLogsInDump
|
||||
self.logsLifespanMillis = logsLifespanMillis
|
||||
}
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
public static func error(tag: String?, _ message: String?, _ functionName: String = #function) {
|
||||
shared.log(level: .fault, tag: tag ?? "nil", message: message ?? "nil", functionName: functionName)
|
||||
}
|
||||
|
||||
public static func warning(tag: String?, _ message: String?, _ functionName: String = #function) {
|
||||
shared.log(level: .error, tag: tag ?? "nil", message: message ?? "nil", functionName: functionName)
|
||||
}
|
||||
|
||||
public static func info(tag: String?, _ message: String?, _ functionName: String = #function) {
|
||||
shared.log(level: .info, tag: tag ?? "nil", message: message ?? "nil", functionName: functionName)
|
||||
}
|
||||
|
||||
public static func debug(tag: String?, _ message: String?, _ functionName: String = #function) {
|
||||
shared.log(level: .debug, tag: tag ?? "nil", message: message ?? "nil", functionName: functionName)
|
||||
}
|
||||
|
||||
public static func verbose(tag: String?, _ message: String?, _ functionName: String = #function) {
|
||||
shared.log(level: .debug, tag: tag ?? "nil", message: message ?? "nil", functionName: functionName)
|
||||
}
|
||||
|
||||
@available(iOS 15.0, *)
|
||||
public func dumpLog() async -> String {
|
||||
return (try? await getLogEntries()) ?? ""
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func log(level: OSLogType, tag: String, message: String, functionName: String) {
|
||||
let formattedMessage = "[\(tag)] \(functionName): \(message)"
|
||||
switch level {
|
||||
case .fault: logger.fault("\(formattedMessage, privacy: .public)")
|
||||
case .error: logger.error("\(formattedMessage, privacy: .public)")
|
||||
case .default: logger.notice("\(formattedMessage, privacy: .public)")
|
||||
case .info: logger.info("\(formattedMessage, privacy: .public)")
|
||||
default: logger.debug("\(formattedMessage, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve log entries from a specified time.
|
||||
/// - Returns: String of log entries, newlines separated
|
||||
@available(iOS 15.0, *)
|
||||
private func getLogEntries() async throws -> String {
|
||||
let logTask = Task.init(priority: .utility) { () -> String in
|
||||
let logs = try retrieveLogEntries()
|
||||
let text = logs
|
||||
.compactMap { "\(Self.dumpDateFormatter.string(from: $0.date)) [\($0.level)] \($0.composedMessage)" }
|
||||
.joined(separator: "\n")
|
||||
return text
|
||||
}
|
||||
return try await logTask.value
|
||||
}
|
||||
|
||||
@available(iOS 15.0, *)
|
||||
private func retrieveLogEntries() throws -> [OSLogEntryLog] {
|
||||
// Open the log store.
|
||||
let logStore = try OSLogStore(scope: .currentProcessIdentifier)
|
||||
|
||||
// Fetch log objects from the given time interval
|
||||
let intervalPosition = logStore.position(date: Date().addingTimeInterval(TimeInterval(-logsLifespanMillis / 1000)))
|
||||
let allEntries = try logStore.getEntries(at: intervalPosition)
|
||||
|
||||
// Filter the log to be relevant for our specific subsystem
|
||||
// and remove other elements (signposts, etc).
|
||||
return allEntries
|
||||
.compactMap { $0 as? OSLogEntryLog }
|
||||
.filter { $0.subsystem == subsystem }
|
||||
.suffix(maxNumberOfLogsInDump)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 15.0, *)
|
||||
extension OSLogEntryLog.Level: @retroactive CustomStringConvertible {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .fault: return "FAULT"
|
||||
case .error: return "ERROR"
|
||||
case .notice: return "WARNING"
|
||||
case .info: return "INFO"
|
||||
case .debug: return "DEBUG"
|
||||
case .undefined: return "UNDEFINED"
|
||||
@unknown default:
|
||||
return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,4 +29,14 @@ class AppPreferences: NSObject {
|
||||
get { UserDefaults.standard.bool(forKey: UserDefaults.AlertsShowCardOptions) }
|
||||
set { UserDefaults.standard.set(newValue, forKey: UserDefaults.AlertsShowCardOptions) }
|
||||
}
|
||||
|
||||
var mapPinStyle: MapPinStyle {
|
||||
get {
|
||||
let saved = UserDefaults.standard.integer(forKey: UserDefaults.MapPinStyle)
|
||||
return MapPinStyle(rawValue: saved) ?? .circle
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(newValue.rawValue, forKey: UserDefaults.MapPinStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public class EQNUserDefaultsCommand: EQNCommandProtocol {
|
||||
migrationV5_8()
|
||||
migrationFirstAppStat()
|
||||
migrationCriticalAlerts()
|
||||
migrationV5_9()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
@@ -30,7 +31,7 @@ public class EQNUserDefaultsCommand: EQNCommandProtocol {
|
||||
|
||||
// seismic card settings
|
||||
if UserDefaults.standard.array(forKey: EQNUserDefaultKeySesmicInformations) == nil {
|
||||
let informations: [SeismicNetworkTableViewCell.InformationType] = [.buttons, .distance, .coordinate, .population]
|
||||
let informations: [SeismicNetworkTableViewCell.InformationType] = [.buttons, .distance, .coordinate, .population, .intensityMap]
|
||||
UserDefaults.standard.set(informations.map { $0.rawValue }, forKey: EQNUserDefaultKeySesmicInformations)
|
||||
}
|
||||
}
|
||||
@@ -107,4 +108,24 @@ public class EQNUserDefaultsCommand: EQNCommandProtocol {
|
||||
|
||||
userDefaults.set(true, forKey: UserDefaults.AppMigrationV5_8_2)
|
||||
}
|
||||
|
||||
private func migrationV5_9() {
|
||||
let userDefaults = UserDefaults.standard
|
||||
let migrationPerformed = userDefaults.bool(forKey: UserDefaults.AppMigrationV5_9)
|
||||
if migrationPerformed {
|
||||
print("[EQNUserDefaultsCommand] Migration v5.9 already performed")
|
||||
return
|
||||
}
|
||||
|
||||
if let saved = userDefaults.array(forKey: EQNUserDefaultKeySesmicInformations) as? [Int] {
|
||||
var informations = saved.compactMap { SeismicNetworkTableViewCell.InformationType(rawValue: $0) }
|
||||
if !informations.contains(.intensityMap) {
|
||||
informations.append(.intensityMap)
|
||||
print("[EQNUserDefaultsCommand] Add intensityMap to seismic informations")
|
||||
}
|
||||
userDefaults.set(informations.map { $0.rawValue }, forKey: EQNUserDefaultKeySesmicInformations)
|
||||
}
|
||||
|
||||
userDefaults.set(true, forKey: UserDefaults.AppMigrationV5_9)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import Foundation
|
||||
case inRadius
|
||||
case positionRelevant
|
||||
case worldWide
|
||||
case userFelt
|
||||
}
|
||||
|
||||
enum Sort: Int {
|
||||
@@ -173,11 +174,16 @@ import Foundation
|
||||
} else if magnitude < 1.5 && distance > 20 {
|
||||
keep = false
|
||||
}
|
||||
} else {
|
||||
} else if filterOption == .worldWide {
|
||||
//filtro che mostra tutti i sismi a livello mondiale di magnitudo>=2
|
||||
if magnitude < 2 {
|
||||
keep = false
|
||||
}
|
||||
} else if filterOption == .userFelt {
|
||||
//filtro che mostra i sismi segnalati da più di 1 utente
|
||||
if seismic.userNumber.intValue < 2 {
|
||||
keep = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// EQNShakemap.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 27/02/25.
|
||||
// Copyright © 2025 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct EQNShakemap: Decodable {
|
||||
let lat: [Int]
|
||||
let lon: [Int]
|
||||
let intensity: Float
|
||||
}
|
||||
@@ -33,6 +33,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, strong) NSNumber *preliminary;
|
||||
@property (nonatomic, strong) NSNumber *smartphoneNumber;
|
||||
@property (nonatomic, strong) NSNumber *userNumber;
|
||||
/// Code to show "intensity map"
|
||||
@property (nonatomic, strong) NSString *isoCode;
|
||||
|
||||
- (instancetype)initWithInfo:(NSDictionary *)info;
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
self.preliminary = info[@"py"];
|
||||
self.smartphoneNumber = info[@"sm"];
|
||||
self.userNumber = info[@"rp"];
|
||||
|
||||
self.isoCode = info[@"iso"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -65,6 +67,7 @@
|
||||
[encoder encodeObject:self.preliminary forKey:@"preliminary"];
|
||||
[encoder encodeObject:self.smartphoneNumber forKey:@"smartphoneNumber"];
|
||||
[encoder encodeObject:self.userNumber forKey:@"userNumber"];
|
||||
[encoder encodeObject:self.isoCode forKey:@"isoCode"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)decoder
|
||||
@@ -86,6 +89,7 @@
|
||||
self.preliminary = [decoder decodeObjectForKey:@"preliminary"];
|
||||
self.smartphoneNumber = [decoder decodeObjectForKey:@"smartphoneNumber"];
|
||||
self.userNumber = [decoder decodeObjectForKey:@"userNumber"];
|
||||
self.isoCode = [decoder decodeObjectForKey:@"isoCode"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
{
|
||||
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
||||
formatter.timeZone = [NSTimeZone timeZoneWithName:@"Europe/Rome"];
|
||||
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US"];
|
||||
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
|
||||
return [formatter dateFromString:dateString];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// EQNMapAnnotationShakemap.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 27/02/25.
|
||||
// Copyright © 2025 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import MapKit
|
||||
|
||||
class EQNMapAnnotationShakemap: NSObject, MKAnnotation {
|
||||
let coordinate: CLLocationCoordinate2D
|
||||
let shakemap: EQNShakemap
|
||||
var intensityColor: UIColor?
|
||||
var intensityTextColor: UIColor?
|
||||
|
||||
let title: String?
|
||||
let subtitle: String? = nil
|
||||
|
||||
|
||||
init(
|
||||
coordinate: CLLocationCoordinate2D,
|
||||
shakemap: EQNShakemap
|
||||
) {
|
||||
self.coordinate = coordinate
|
||||
self.shakemap = shakemap
|
||||
self.title = Self.title(for: shakemap.intensity)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private static func title(for intensity: Float) -> String {
|
||||
return switch intensity {
|
||||
case 0..<2.5: NSLocalizedString("mercalli_II", comment: "")
|
||||
case 2.5..<3.5: NSLocalizedString("mercalli_III", comment: "")
|
||||
case 3.5..<4.5: NSLocalizedString("mercalli_IV", comment: "")
|
||||
case 4.5..<5.5: NSLocalizedString("mercalli_V", comment: "")
|
||||
case 5.5..<6.5: NSLocalizedString("mercalli_VI", comment: "")
|
||||
case 6.5..<7.5: NSLocalizedString("mercalli_VII", comment: "")
|
||||
case 7.5..<8.5: NSLocalizedString("mercalli_VIII", comment: "")
|
||||
case 8.5..<9.5: NSLocalizedString("mercalli_IX", comment: "")
|
||||
case 9.5..<10.5: NSLocalizedString("mercalli_X", comment: "")
|
||||
case 10.5..<11.5: NSLocalizedString("mercalli_XI", comment: "")
|
||||
default: NSLocalizedString("mercalli_XII", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// MapPinStyle.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 28/02/25.
|
||||
// Copyright © 2025 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum MapPinStyle: Int, CaseIterable {
|
||||
case circle
|
||||
case light
|
||||
case full
|
||||
|
||||
mutating func next() {
|
||||
let all = Self.allCases
|
||||
let idx = all.firstIndex(of: self)!
|
||||
let next = all.index(after: idx)
|
||||
let newValue = all[next == all.endIndex ? all.startIndex : next]
|
||||
self = newValue
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// APIService.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 27/02/25.
|
||||
// Copyright © 2025 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Shogun
|
||||
|
||||
|
||||
fileprivate enum EQNetworkAPI {
|
||||
static let scheme = "https"
|
||||
static let host = "cache.earthquakenetwork.it"
|
||||
static let timeout: TimeInterval = 20.0
|
||||
}
|
||||
|
||||
|
||||
|
||||
class APIService {
|
||||
|
||||
static let shared = APIService()
|
||||
|
||||
private enum Endpoint: String {
|
||||
case shakemap = "distquake_download_shakemap.php"
|
||||
|
||||
var method: HttpMethod {
|
||||
.get
|
||||
}
|
||||
}
|
||||
|
||||
private enum HttpMethod: String {
|
||||
case get = "GET"
|
||||
case post = "POST"
|
||||
case put = "PUT"
|
||||
case delete = "DELETE"
|
||||
case patch = "PATCH"
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func fetchShakemap(
|
||||
isoCode: String
|
||||
) async throws -> [EQNShakemap] {
|
||||
try await performRequest(
|
||||
endpoint: .shakemap,
|
||||
paramaters: ["iso_id": isoCode]
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func performRequest<T: Decodable>(
|
||||
endpoint: Endpoint,
|
||||
paramaters: [String: String] = [:],
|
||||
body: Encodable? = nil
|
||||
) async throws -> T {
|
||||
var components = makeComponents(for: endpoint)
|
||||
|
||||
// add query string parameters to the existing ones
|
||||
let queryParameters = paramaters.map { key, value in URLQueryItem(name: key, value: value) }
|
||||
let queryItems = components.queryItems ?? []
|
||||
components.queryItems = queryItems + queryParameters
|
||||
|
||||
guard let url = components.url else {
|
||||
Log.error(tag: Self.TAG, "Unable to create URL for the request")
|
||||
throw "Unable to create url for the request"
|
||||
}
|
||||
|
||||
var request = URLRequest(
|
||||
url: url,
|
||||
cachePolicy: .reloadIgnoringLocalCacheData,
|
||||
timeoutInterval: EQNetworkAPI.timeout
|
||||
)
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.setValue("application/json", forHTTPHeaderField: "Accept")
|
||||
request.httpMethod = endpoint.method.rawValue
|
||||
|
||||
Log.debug(tag: Self.TAG, "Request url: \(url)")
|
||||
|
||||
if let body {
|
||||
do {
|
||||
let bodyData = try JSONEncoder().encode(body)
|
||||
request.httpBody = bodyData
|
||||
} catch {
|
||||
Log.error(tag: Self.TAG, "Unable to encode body data: \(error)")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
let response = try await URLSession.shared.data(for: request).0
|
||||
let responseString = String(data: response, encoding: .utf8) ?? "No data"
|
||||
Log.info(tag: Self.TAG, responseString)
|
||||
return try JSONDecoder().decode(T.self, from: response)
|
||||
} catch {
|
||||
Log.error(tag: Self.TAG, "Decoding error: \(error)")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
private func makeComponents(for endpoint: Endpoint) -> URLComponents {
|
||||
var components = URLComponents()
|
||||
components.scheme = EQNetworkAPI.scheme
|
||||
components.host = EQNetworkAPI.host
|
||||
components.path = "/" + endpoint.rawValue
|
||||
return components
|
||||
}
|
||||
}
|
||||
|
||||
extension APIService: Loggable { }
|
||||
|
||||
/// Easily throw generic errors with a text description.
|
||||
extension String: @retroactive Error {}
|
||||
extension String: @retroactive LocalizedError {
|
||||
public var errorDescription: String? {
|
||||
return self
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="CWo-PE-Dqp">
|
||||
<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" initialViewController="CWo-PE-Dqp">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
@@ -122,28 +122,28 @@
|
||||
<blurEffect style="light"/>
|
||||
</visualEffectView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rX7-cf-eHr">
|
||||
<rect key="frame" x="41.5" y="313" width="331" height="270.5"/>
|
||||
<rect key="frame" x="31" y="313" width="352" height="270.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Card settings" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pYc-TM-AIW">
|
||||
<rect key="frame" x="12" y="12" width="307" height="33.5"/>
|
||||
<rect key="frame" x="12" y="12" width="328" height="33.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
|
||||
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="Dzd-L1-MsF">
|
||||
<rect key="frame" x="12" y="75.5" width="307" height="133"/>
|
||||
<rect key="frame" x="12" y="75.5" width="328" height="133"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="dKu-4z-j0d">
|
||||
<rect key="frame" x="0.0" y="0.0" width="307" height="31"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="328" height="31"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Distance" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dRl-jP-icD">
|
||||
<rect key="frame" x="0.0" y="0.0" width="238" height="31"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="259" height="31"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g8g-7l-bCj">
|
||||
<rect key="frame" x="258" y="0.0" width="51" height="31"/>
|
||||
<rect key="frame" x="279" y="0.0" width="51" height="31"/>
|
||||
<connections>
|
||||
<action selector="switchChanged:" destination="Rfp-kt-2Kx" eventType="valueChanged" id="bzY-wG-8Qw"/>
|
||||
</connections>
|
||||
@@ -151,16 +151,16 @@
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="toG-0g-rVB">
|
||||
<rect key="frame" x="0.0" y="51" width="307" height="31"/>
|
||||
<rect key="frame" x="0.0" y="51" width="328" height="31"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Coordinates" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vyf-82-r1X">
|
||||
<rect key="frame" x="0.0" y="0.0" width="238" height="31"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="259" height="31"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Jjd-6X-t3e">
|
||||
<rect key="frame" x="258" y="0.0" width="51" height="31"/>
|
||||
<rect key="frame" x="279" y="0.0" width="51" height="31"/>
|
||||
<connections>
|
||||
<action selector="switchChanged:" destination="Rfp-kt-2Kx" eventType="valueChanged" id="QI3-0U-fDp"/>
|
||||
</connections>
|
||||
@@ -168,16 +168,16 @@
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="CU8-Db-J5u">
|
||||
<rect key="frame" x="0.0" y="102" width="307" height="31"/>
|
||||
<rect key="frame" x="0.0" y="102" width="328" height="31"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Population" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Hpe-Qx-6yX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="238" height="31"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="259" height="31"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="N7g-C0-b1h">
|
||||
<rect key="frame" x="258" y="0.0" width="51" height="31"/>
|
||||
<rect key="frame" x="279" y="0.0" width="51" height="31"/>
|
||||
<connections>
|
||||
<action selector="switchChanged:" destination="Rfp-kt-2Kx" eventType="valueChanged" id="U53-Iv-BcF"/>
|
||||
</connections>
|
||||
@@ -195,7 +195,7 @@
|
||||
</constraints>
|
||||
</stackView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="l7i-w5-sp0">
|
||||
<rect key="frame" x="219" y="228.5" width="100" height="30"/>
|
||||
<rect key="frame" x="219" y="228.5" width="121" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="2j0-dH-u0p"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="100" id="Dpt-7I-vJu"/>
|
||||
@@ -227,7 +227,7 @@
|
||||
<constraint firstItem="8QE-80-TMN" firstAttribute="leading" secondItem="1tH-EZ-OWj" secondAttribute="leading" id="FLK-lL-lW7"/>
|
||||
<constraint firstItem="rX7-cf-eHr" firstAttribute="centerX" secondItem="Q06-4a-yjE" secondAttribute="centerX" id="LDx-5Z-nve"/>
|
||||
<constraint firstItem="1tH-EZ-OWj" firstAttribute="trailing" secondItem="8QE-80-TMN" secondAttribute="trailing" id="NEe-0Z-Ut5"/>
|
||||
<constraint firstItem="rX7-cf-eHr" firstAttribute="width" secondItem="Q06-4a-yjE" secondAttribute="width" multiplier="0.8" id="P27-yy-HrC"/>
|
||||
<constraint firstItem="rX7-cf-eHr" firstAttribute="width" secondItem="Q06-4a-yjE" secondAttribute="width" multiplier="0.85" id="P27-yy-HrC"/>
|
||||
<constraint firstItem="8QE-80-TMN" firstAttribute="top" secondItem="Q06-4a-yjE" secondAttribute="top" id="gh4-42-aNP"/>
|
||||
<constraint firstItem="rX7-cf-eHr" firstAttribute="centerY" secondItem="Q06-4a-yjE" secondAttribute="centerY" id="kEj-37-bWM"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8QE-80-TMN" secondAttribute="bottom" id="vey-5u-Ovd"/>
|
||||
@@ -433,16 +433,16 @@
|
||||
<blurEffect style="light"/>
|
||||
</visualEffectView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dnj-0w-OVz">
|
||||
<rect key="frame" x="41.5" y="112" width="331" height="672"/>
|
||||
<rect key="frame" x="31" y="112" width="352" height="672"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Filters" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3tl-cp-QLk">
|
||||
<rect key="frame" x="12" y="16" width="307" height="20.5"/>
|
||||
<rect key="frame" x="12" y="16" width="328" height="20.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="MVi-r1-SFm">
|
||||
<rect key="frame" x="276" y="630" width="39" height="30"/>
|
||||
<rect key="frame" x="297" y="630" width="39" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="NoD-eT-hcR"/>
|
||||
</constraints>
|
||||
@@ -452,7 +452,7 @@
|
||||
</connections>
|
||||
</button>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="UD6-Ee-0GM">
|
||||
<rect key="frame" x="0.0" y="46.5" width="331" height="573.5"/>
|
||||
<rect key="frame" x="0.0" y="46.5" width="352" height="573.5"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="6LP-zk-O1z" id="9ZD-Dr-D9f"/>
|
||||
@@ -481,7 +481,7 @@
|
||||
<constraint firstItem="cc3-jb-kKt" firstAttribute="leading" secondItem="Lp8-R3-fW9" secondAttribute="leading" id="A0P-4p-dRN"/>
|
||||
<constraint firstAttribute="bottom" secondItem="cc3-jb-kKt" secondAttribute="bottom" id="EO3-iK-i5i"/>
|
||||
<constraint firstItem="dnj-0w-OVz" firstAttribute="height" secondItem="9GQ-SZ-2Mt" secondAttribute="height" multiplier="0.75" id="EWy-XB-uoC"/>
|
||||
<constraint firstItem="dnj-0w-OVz" firstAttribute="width" secondItem="9GQ-SZ-2Mt" secondAttribute="width" multiplier="0.8" id="ZHH-KA-oym"/>
|
||||
<constraint firstItem="dnj-0w-OVz" firstAttribute="width" secondItem="9GQ-SZ-2Mt" secondAttribute="width" multiplier="0.85" id="ZHH-KA-oym"/>
|
||||
<constraint firstItem="dnj-0w-OVz" firstAttribute="centerX" secondItem="9GQ-SZ-2Mt" secondAttribute="centerX" id="fUr-JO-gPl"/>
|
||||
<constraint firstItem="Lp8-R3-fW9" firstAttribute="trailing" secondItem="cc3-jb-kKt" secondAttribute="trailing" id="gfa-oV-YnD"/>
|
||||
<constraint firstItem="dnj-0w-OVz" firstAttribute="centerY" secondItem="9GQ-SZ-2Mt" secondAttribute="centerY" id="i3x-eo-Lvg"/>
|
||||
|
||||
@@ -12,11 +12,6 @@ import MapKit
|
||||
|
||||
class EQNSeismicAnnotationView: MKAnnotationView {
|
||||
|
||||
static let IdentifierFull = "EQNSeismicAnnotationViewFull"
|
||||
static let IdentifierLight = "EQNSeismicAnnotationViewLight"
|
||||
static let IdentifierCircle = "EQNSeismicAnnotationViewCircle"
|
||||
|
||||
|
||||
private static let LabelHeight: CGFloat = 15.0
|
||||
private static let MagnitudeHeight: CGFloat = 25.0
|
||||
private static let MagnitudeWidth: CGFloat = 45.0
|
||||
@@ -43,11 +38,16 @@ class EQNSeismicAnnotationView: MKAnnotationView {
|
||||
get { magnitudeLabel.text }
|
||||
}
|
||||
|
||||
var magnitudeColor: UIColor {
|
||||
var magnitudeTextColor: UIColor {
|
||||
set { magnitudeLabel.textColor = newValue }
|
||||
get { magnitudeLabel.textColor }
|
||||
}
|
||||
|
||||
var magnitudeBackgroundColor: UIColor? {
|
||||
set { magnitudeView.backgroundColor = newValue }
|
||||
get { magnitudeView.backgroundColor }
|
||||
}
|
||||
|
||||
var isUserSelection: Bool = false {
|
||||
didSet {
|
||||
magnitudeView.layer.borderColor = isUserSelection ? AppTheme.Colors.red.cgColor : AppTheme.Colors.darkGray.cgColor
|
||||
@@ -107,13 +107,13 @@ class EQNSeismicAnnotationView: MKAnnotationView {
|
||||
|
||||
backgroundColor = .clear
|
||||
|
||||
if reuseIdentifier == Self.IdentifierFull {
|
||||
if reuseIdentifier == Self.identifier(for: .full) {
|
||||
frame = CGRect(x: 0, y: 0, width: Self.FullViewWidth, height: Self.FullViewHeight)
|
||||
setupFullUI()
|
||||
} else if reuseIdentifier == Self.IdentifierLight {
|
||||
} else if reuseIdentifier == Self.identifier(for: .light) {
|
||||
frame = CGRect(x: 0, y: 0, width: Self.SmallViewWidth, height: Self.SmallViewHeight)
|
||||
setupLightUI()
|
||||
} else if reuseIdentifier == Self.IdentifierCircle {
|
||||
} else if reuseIdentifier == Self.identifier(for: .circle) {
|
||||
frame = CGRect(x: 0, y: 0, width: Self.CircleViewHeight, height: Self.CircleViewHeight)
|
||||
setupCircleUI()
|
||||
}
|
||||
@@ -158,3 +158,14 @@ class EQNSeismicAnnotationView: MKAnnotationView {
|
||||
addSubview(imageView)
|
||||
}
|
||||
}
|
||||
|
||||
extension EQNSeismicAnnotationView {
|
||||
|
||||
static func identifier(for style: MapPinStyle) -> String {
|
||||
return switch style {
|
||||
case .circle: "EQNSeismicAnnotationViewCircle"
|
||||
case .light: "EQNSeismicAnnotationViewLight"
|
||||
case .full: "EQNSeismicAnnotationViewFull"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"filter_show_area" = "إظهار كافة الزلازل ضمن دائرة نصف قطرها:";
|
||||
"filter_show_relevant" = "إظهار الزلازل ذات الصلة بموقعي فقط";
|
||||
"filter_show_all" = "عرض جميع الزلازل على مستوى العالم (M≥2)";
|
||||
"filter_show_felt" = "إظهار الزلازل التي يشعر بها المستخدمون فقط";
|
||||
"filter_minimum_magnitude" = "والحجم الأدنى:";
|
||||
"main_understood" = "مفهوم";
|
||||
"options_low_magnitude" = "الانتباه إلى أن ليست كل الشبكات الزلزالية توفر بيانات الزلازل التي تقل عن 2.0 درجة. علاوة على ذلك ، فإنك تزيد بشكل كبير من نقل البيانات واستخدام البطارية بسبب الإخطارات. ما لم يتم تصنيع جهازك حديثا ، ستلاحظ أيضا تباطؤا عاما.";
|
||||
@@ -53,6 +54,7 @@
|
||||
"filter_area" = "الزلازل المعروضة: في دائرة نصف قطرها";
|
||||
"filter_relevant" = "الزلازل المعروضة: ذات صلة";
|
||||
"filter_all" = "الزلازل المعروضة: الكل";
|
||||
"filter_felt" = "الزلازل المعروضة: شعرت";
|
||||
"liveview_unknown_location" = "موقفك غير معروف. تمكين موقع الهاتف الذكي من تكوين الهاتف الذكي";
|
||||
"map_number" = "تم الكشف عن زلزال بواسطة %@ الهواتف الذكية";
|
||||
"permission_location_no" = "لقد اخترت منع التطبيق من قراءة موقع الجهاز. لن تستلم تنبيهات وإشعارات في الوقت الفعلي";
|
||||
@@ -142,6 +144,8 @@
|
||||
"mercalli_XI" = "\U200FXI - تدمير مراكز حضرية بأكملها ، العديد من الضحايا ، شقوق في الأرض وانهيارات أرضيةlandslides";
|
||||
"mercalli_XII" = "\U200FXII - اضطراب التربة ، إزاحة قشرة الأرض";
|
||||
"mercalli_intensity" = "شدة %@";
|
||||
"shakemap" = "خريطة اهتزازية";
|
||||
"shakemap_description" = "خريطة الكثافة بناءً على التقارير المحسوسة";
|
||||
|
||||
// values
|
||||
"official_magnitude_value_00" = "درجة >= 0.0";
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"filter_show_area" = "Εμφάνιση όλων των σεισμών σε ακτίνα:";
|
||||
"filter_show_relevant" = "Εμφάνιση μόνο των σχετικών σεισμών σε σχέση με την τοποθεσία μου";
|
||||
"filter_show_all" = "Εμφάνιση όλων των σεισμών παγκοσμίως (M≥2)";
|
||||
"filter_show_felt" = "Εμφάνιση μόνο των σεισμών που αισθάνθηκαν οι χρήστες";
|
||||
"filter_minimum_magnitude" = "και ελάχιστο μέγεθος:";
|
||||
"main_understood" = "Κατάλαβα";
|
||||
"options_low_magnitude" = "Λάβε υπόψη ότι δεν παρέχουν όλα τα σεισμικά δίκτυα δεδομένα για σεισμούς με μέγεθος κάτω από 2.0. Επίσης, αυξάνεις σημαντικά την μεταφορά δεδομένων και την χρήση μπαταρίας λόγω κοινοποιήσεων. Αν η συσκευή σου δεν είναι πρόσφατης κατασκευής, θα παρατηρήσεις επίσης μια γενική επιβράδυνση.";
|
||||
@@ -53,6 +54,7 @@
|
||||
"filter_area" = "Εμφανίζονται σεισμοί: στην ακτίνα";
|
||||
"filter_relevant" = "Εμφανίζονται σεισμοί: σχετικοί";
|
||||
"filter_all" = "Εμφανίζονται σεισμοί: όλοι (M≥2)";
|
||||
"filter_felt" = "Εμφανίζονται σεισμοί: αισθητοί";
|
||||
"liveview_unknown_location" = "Η θέση σου είναι άγνωστη. Ενεργοποίησε την τοποθεσία του smartphone από την διαμόρφωση του smartphone";
|
||||
"map_number" = "Ανιχνεύθηκε δόνηση από %@ smartphone";
|
||||
"permission_location_no" = "Έχεις επιλέξει να αποτρέπεις την εφαρμογή από την ανάγνωση της τοποθεσίας της συσκευής. ΔΕΝ θα λαμβάνεις κοινοποιήσεις και ειδοποιήσεις σε πραγματικό χρόνο";
|
||||
@@ -142,6 +144,8 @@
|
||||
"mercalli_XI" = "XI - Καταστροφές ολόκληρων αστικών κέντρων, πολλά θύματα, ρωγμές στο έδαφος και κατολισθήσεις";
|
||||
"mercalli_XII" = "XII - Διάσπαση του εδάφους, μετατόπιση του φλοιού της γης";
|
||||
"mercalli_intensity" = "Ένταση %@";
|
||||
"shakemap" = "Χάρτης έντασης";
|
||||
"shakemap_description" = "Χάρτης έντασης με βάση τις αναφορές χρηστών";
|
||||
|
||||
// values
|
||||
"official_magnitude_value_00" = "Μέγεθος >= 0.0";
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"filter_show_area" = "Show all earthquakes within a radius of:";
|
||||
"filter_show_relevant" = "Show only the relevant earthquakes with respect to my location";
|
||||
"filter_show_all" = "Show all earthquakes globally (M≥2)";
|
||||
"filter_show_felt" = "Show only the earthquakes felt by the users";
|
||||
"filter_minimum_magnitude" = "and minimum magnitude:";
|
||||
"main_understood" = "Understood";
|
||||
"options_low_magnitude" = "Beware that not all seismic networks provide earthquake data below magnitude 2.0. Moreover, you significantly increase the data transfer and the battery usage due to notifications. Unless your device is recently manufactured, you will also notice a general slowdown.";
|
||||
@@ -53,6 +54,7 @@
|
||||
"filter_area" = "Quakes shown: in the radius";
|
||||
"filter_relevant" = "Quakes shown: relevant";
|
||||
"filter_all" = "Quakes shown: all (M≥2)";
|
||||
"filter_felt" = "Quakes shown: felt";
|
||||
"liveview_unknown_location" = "Your position is unknown. Enable smartphone location from smartphone configuration";
|
||||
"map_number" = "Quake detected by %@ smartphones";
|
||||
"permission_location_no" = "You have chosen to prevent the app from reading the location of the device. You will NOT receive real-time notifications and alerts";
|
||||
@@ -142,6 +144,8 @@
|
||||
"mercalli_XI" = "XI - Destruction of entire urban centers, many victims, crevices in the ground and landslides";
|
||||
"mercalli_XII" = "XII - Disruption of the soil, displacement of the earth's crust";
|
||||
"mercalli_intensity" = "Intensity %@";
|
||||
"shakemap" = "Shakemap";
|
||||
"shakemap_description" = "Intensity map based on user reports";
|
||||
|
||||
// values
|
||||
"official_magnitude_value_00" = "Magnitude >= 0.0";
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"filter_show_area" = "Mostrar todos los sismos en un radio de:";
|
||||
"filter_show_relevant" = "Mostrar solo los sismos relevantes con respecto a mi ubicación";
|
||||
"filter_show_all" = "Mostrar todos los sismos globalmente (M≥2)";
|
||||
"filter_show_felt" = "Mostrar solo los sismos sentidos por los usuarios";
|
||||
"filter_minimum_magnitude" = "y magnitud mínima:";
|
||||
"main_understood" = "Entendido";
|
||||
"options_low_magnitude" = "Tenga en cuenta que no todas las redes sísmicas proporcionan datos para sismos de magnitud inferior a 2.0. Además, aumenta significativamente la transferencia de datos con el servidor y el uso de la batería debido a la mayor cantidad de notificaciones. Si tu dispositivo no se ha fabricado recientemente, también notará una desaceleración general de la aplicación.";
|
||||
@@ -53,6 +54,7 @@
|
||||
"filter_area" = "Sismos mostrados: en el radio";
|
||||
"filter_relevant" = "Sismos mostrados: relevantes";
|
||||
"filter_all" = "Sismos mostrados: todos (M≥2)";
|
||||
"filter_felt" = "Sismos mostrados: sentidos";
|
||||
"liveview_unknown_location" = "Tu posición es desconocida. Habilitar la ubicación del smartphone desde la página de configuración del smartphone";
|
||||
"map_number" = "Sismo detectado por %@ smartphones";
|
||||
"permission_location_no" = "Ha elegido evitar que la aplicación lea la ubicación de tu dispositivo. NO recibirá notificaciones y alertas en tiempo real";
|
||||
@@ -142,6 +144,8 @@
|
||||
"mercalli_XI" = "XI - Destrucción de centros urbanos enteros, muchas víctimas, grietas en el suelo y deslizamientos de tierra";
|
||||
"mercalli_XII" = "XII - Alteración del suelo, desplazamiento de la corteza terrestre.";
|
||||
"mercalli_intensity" = "Intensidad %@";
|
||||
"shakemap" = "Mapa intensidad";
|
||||
"shakemap_description" = "Mapa de intensidad basado en informes de usuarios";
|
||||
|
||||
// values
|
||||
"official_magnitude_value_00" = "Magnitud >= 0.0";
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"filter_show_area" = "Afficher tous les tremblements de terre dans un rayon de :";
|
||||
"filter_show_relevant" = "Afficher uniquement les tremblements de terre pertinents par rapport à ma position";
|
||||
"filter_show_all" = "Afficher tous les tremblements de terre globalement (M≥2)";
|
||||
"filter_show_felt" = "Afficher uniquement les tremblements de terre ressentis par les utilisateurs";
|
||||
"filter_minimum_magnitude" = "et magnitude minimale :";
|
||||
"main_understood" = "J'ai compris";
|
||||
"options_low_magnitude" = "Attention, les réseaux sismiques ne fournissent pas tous des données pour des séismes de magnitude inférieure à 2,0. De plus, vous augmentez significativement le transfert de données et l'utilisation de la batterie en raison du plus grand nombre de notifications. Sauf si votre appareil est de fabrication récente, vous remarquerez également un ralentissement général.";
|
||||
@@ -53,6 +54,7 @@
|
||||
"filter_area" = "Séismes affichés : dans le rayon";
|
||||
"filter_relevant" = "Séismes affichés : pertinents";
|
||||
"filter_all" = "Séismes affichés : tous (M≥2)";
|
||||
"filter_felt" = "Séismes affichés : ressentis";
|
||||
"liveview_unknown_location" = "Votre position est inconnue. Activez la localisation à partir de la page de configuration de votre appareil";
|
||||
"map_number" = "Séisme détecté par %@ smartphones";
|
||||
"permission_location_no" = "Vous avez choisi d'empêcher l'app de lire la position de votre appareil. Vous ne recevrez PAS de notifications et d'alertes en temps réel";
|
||||
@@ -142,6 +144,8 @@
|
||||
"mercalli_XI" = "XI - Destruction de centres urbains entiers, nombreuses victimes, crevasses dans le sol et glissements de terrain";
|
||||
"mercalli_XII" = "XII - Perturbation du sol, déplacement de la croûte terrestre";
|
||||
"mercalli_intensity" = "Intensité %@";
|
||||
"shakemap" = "Carte d'intensité";
|
||||
"shakemap_description" = "Carte d'intensité basée sur les rapports ressentis";
|
||||
|
||||
// values
|
||||
"official_magnitude_value_00" = "Magnitude >= 0.0";
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"filter_show_area" = "Prikaži sve potrese u radijusu od:";
|
||||
"filter_show_relevant" = "Prikaži samo relevantne potrese s obzirom na moju lokaciju";
|
||||
"filter_show_all" = "Prikaži sve potrese globalno (M≥2)";
|
||||
"filter_show_felt" = "Prikaži samo potrese koje su korisnici osjetili";
|
||||
"filter_minimum_magnitude" = "i minimalna veličina:";
|
||||
"main_understood" = "Razumijem";
|
||||
"options_low_magnitude" = "Imajte na umu da ne pružaju sve seizmološke mreže podatke o potresima jačine ispod 2,0. Štoviše, znatno povećavate prijenos podataka i upotrebu baterije zbog obavijesti. Ako vaš uređaj nije nedavno proizveden, primijetit ćete i opće usporavanje.";
|
||||
@@ -53,6 +54,7 @@
|
||||
"filter_area" = "Prikazani potresi: u polumjeru";
|
||||
"filter_relevant" = "Prikazani potresi: relevantni";
|
||||
"filter_all" = "Prikazani potresi: svi (M≥2)";
|
||||
"filter_felt" = "Prikazani potresi: osjetili";
|
||||
"liveview_unknown_location" = "Vaš položaj nije poznat. Omogući lokaciju u konfiguraciji pametnog telefona";
|
||||
"map_number" = "Potres je otkrio sljedeći broj pametnih telefona: %@";
|
||||
"permission_location_no" = "Odlučili ste onemogućiti aplikaciji očitavanje lokacije uređaja. NEĆETE primati obavijesti i upozorenja u stvarnom vremenu";
|
||||
@@ -142,6 +144,8 @@
|
||||
"mercalli_XI" = "XI - Uništavanje čitavih urbanih središta, mnogo žrtava, pukotine u zemlji i klizišta";
|
||||
"mercalli_XII" = "XII - Poremećaj tla, pomicanje zemljine kore";
|
||||
"mercalli_intensity" = "Intenzitet %@";
|
||||
"shakemap" = "Mapa intenziteta";
|
||||
"shakemap_description" = "Karta intenziteta na temelju izvješća o filu";
|
||||
|
||||
// values
|
||||
"official_magnitude_value_00" = "Jačina >= 0.0";
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"filter_show_area" = "Tampilkan semua gempa dalam radius:";
|
||||
"filter_show_relevant" = "Tampilkan hanya gempa bumi yang relevan dengan lokasi saya";
|
||||
"filter_show_all" = "Tampilkan semua gempa bumi secara global (M≥2)";
|
||||
"filter_show_felt" = "Hanya menampilkan gempa bumi yang dirasakan oleh pengguna";
|
||||
"filter_minimum_magnitude" = "dan besaran minimum:";
|
||||
"main_understood" = "Paham";
|
||||
"options_low_magnitude" = "Harap diperhatikan bahwa tidak semua jaringan seismik menyediakan data gempa di bawah magnitudo 2,0 SR. Selain itu, pemberitahuan yang diaktifkan akan menambah transfer data dan penggunaan baterai secara signifikan. Perangkat juga mungkin berjalan lebih lambat, kecuali jika perangkat Anda tergolong baru diproduksi.";
|
||||
@@ -53,6 +54,7 @@
|
||||
"filter_area" = "Gempa yang ditampilkan: dalam radius";
|
||||
"filter_relevant" = "Gempa bumi yang ditampilkan: relevan";
|
||||
"filter_all" = "Gempa bumi ditampilkan: semua (M≥2)";
|
||||
"filter_felt" = "Gempa yang ditampilkan: terasa";
|
||||
"liveview_unknown_location" = "Posisi Anda tidak diketahui. Aktifkan lokasi smartphone dari konfigurasi smartphone";
|
||||
"map_number" = "Gempa terdeteksi oleh %@ smartphone";
|
||||
"permission_location_no" = "Anda telah memilih untuk mencegah aplikasi membaca lokasi perangkat. Anda TIDAK akan menerima pemberitahuan dan peringatan secara real time";
|
||||
@@ -142,6 +144,8 @@
|
||||
"mercalli_XI" = "XI - Penghancuran seluruh pusat kota, banyak korban, celah-celah di tanah dan tanah longsor";
|
||||
"mercalli_XII" = "XII - Gangguan tanah, perpindahan kerak bumi";
|
||||
"mercalli_intensity" = "Intensitas %@";
|
||||
"shakemap" = "Peta intensitas";
|
||||
"shakemap_description" = "Peta intensitas berdasarkan laporan pengguna";
|
||||
|
||||
// values
|
||||
"official_magnitude_value_00" = "Magnitudo >= 0.0";
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"filter_show_area" = "Mostra tutti i sismi nel raggio di:";
|
||||
"filter_show_relevant" = "Mostra solo i sismi rilevanti rispetto alla mia posizione";
|
||||
"filter_show_all" = "Mostra tutti i sismi a livello globale (M≥2)";
|
||||
"filter_show_felt" = "Mostra solo i sismi percepiti dagli utenti";
|
||||
"filter_minimum_magnitude" = "e magnitudo minima:";
|
||||
"main_understood" = "Ho capito";
|
||||
"options_low_magnitude" = "Considera che non tutte le reti sismiche forniscono dati per sismi sotto magnitudo 2.0. Inoltre, incrementi significativamente il trasferimento dati con il server e l'utilizzo batteria dovuto al maggior numero di notifiche. Se il tuo dispositivo non è di recente fabbricazione, noterai anche un generale rallentamento dell'app.";
|
||||
@@ -53,6 +54,7 @@
|
||||
"filter_area" = "Sismi mostrati: nel raggio";
|
||||
"filter_relevant" = "Sismi mostrati: rilevanti";
|
||||
"filter_all" = "Sismi mostrati: tutti (M≥2)";
|
||||
"filter_felt" = "Sismi mostrati: percepiti";
|
||||
"liveview_unknown_location" = "La tua posizione è sconosciuta. Abilita la localizzazione dalla pagina di configurazione del tuo dispositivo";
|
||||
"map_number" = "Sisma rilevato da %@ smartphone";
|
||||
"permission_location_no" = "Hai scelto di impedire alla app di leggere la posizione del tuo dispositivo. NON riceverai notifiche ed allerte in tempo reale.";
|
||||
@@ -142,6 +144,8 @@
|
||||
"mercalli_XI" = "XI - Distruzione di interi centri urbani, moltissime vittime, crepacci nel suolo e frane ";
|
||||
"mercalli_XII" = "XII - Sconvolgimento del suolo, dislocamento della crosta terrestre";
|
||||
"mercalli_intensity" = "Intensità %@";
|
||||
"shakemap" = "Mappa intensità";
|
||||
"shakemap_description" = "Mappa di intensità basata sulle segnalazioni degli utenti";
|
||||
|
||||
// values
|
||||
"official_magnitude_value_00" = "Magnitudo >= 0.0";
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"filter_show_area" = "Aşağıdaki yarıçap içindeki tüm depremleri göster:";
|
||||
"filter_show_relevant" = "Konumuma göre yalnızca ilgili depremleri göster";
|
||||
"filter_show_all" = "Dünyadaki tüm depremleri göster (M≥2)";
|
||||
"filter_show_felt" = "Sadece kullanıcıların hissettiği depremleri göster";
|
||||
"filter_minimum_magnitude" = "ve minimum büyüklük:";
|
||||
"main_understood" = "Anladım";
|
||||
"options_low_magnitude" = "Tüm sismik ağların 2.0'nin altında büyüklüğe sahip depremlerin verilerini sağlamadığını unutmayın. Ayrıca, bildirimler nedeniyle veri aktarımınız ve pil kullanımınız önemli ölçüde artar. Cihazınız yakın zamanda üretilmediği sürece, genel bir yavaşlama da fark edersiniz.";
|
||||
@@ -53,6 +54,7 @@
|
||||
"filter_area" = "Gösterilen depremler: yarıçap içinde";
|
||||
"filter_relevant" = "Gösterilen depremler: alakalı";
|
||||
"filter_all" = "Gösterilen depremler: tümü (M≥2)";
|
||||
"filter_felt" = "Gösterilen depremler: hissedildi";
|
||||
"liveview_unknown_location" = "Konumunuz bilinmiyor. Akıllı telefon yapılandırmasından akıllı telefon konumunu etkinleştirin";
|
||||
"map_number" = "%@ akıllı telefon tarafından tespit edilen deprem";
|
||||
"permission_location_no" = "Uygulamanın cihazın konumunu okumasını engellemeyi seçtiniz. Gerçek zamanlı bildirim ve uyarılar ALMAYACAKSINIZ";
|
||||
@@ -142,6 +144,8 @@
|
||||
"mercalli_XI" = "XI - Tüm şehir merkezlerinin, birçok kurbanın, zemindeki yarıkların ve toprak kaymalarının yok edilmesi";
|
||||
"mercalli_XII" = "XII - Toprağın bozulması, yer kabuğunun yer değiştirmesi";
|
||||
"mercalli_intensity" = "Şiddeti %@";
|
||||
"shakemap" = "Yoğunluk haritası";
|
||||
"shakemap_description" = "Kullanıcı raporlarına dayalı yoğunluk haritası";
|
||||
|
||||
// values
|
||||
"official_magnitude_value_00" = "Büyüklük >= 0.0";
|
||||
|
||||
Reference in New Issue
Block a user