Compare commits

...

21 Commits

Author SHA1 Message Date
Andrea Busi a2f740b0a8 release: Increase version for release 2025-03-07 17:57:54 +01:00
Andrea Busi 9cf93e652b fix: Reset cell informations 2025-03-07 15:38:51 +01:00
Andrea Busi 2d23056ba8 release: Increase version for release 2025-03-07 14:10:47 +01:00
Andrea Busi cb6ecca774 refactor: Hide description label when taking screenshot in intensity map 2025-03-07 14:10:47 +01:00
Andrea Busi 96286a49f6 feat: Upgrade Xcode project version 2025-03-07 14:00:28 +01:00
Andrea Busi 481e8a28c0 fix: Solve crash with a single shakemape curve 2025-03-07 14:00:16 +01:00
Andrea Busi 286a4062f5 release: Increase version for release 2025-03-07 11:16:35 +01:00
Andrea Busi 01a8ad7419 fix: Solve wrong calculation in scroll indicator 2025-03-07 11:16:07 +01:00
Andrea Busi 6e97e9bd2c release: Increase version for release 2025-03-07 09:56:03 +01:00
Andrea Busi af6e94efcb fix: Solve non working intensity map in minimal cell 2025-03-07 09:45:45 +01:00
Andrea Busi 5387758449 fix: Select yearly as default subscription 2025-03-07 09:42:26 +01:00
Andrea Busi 054603b42d release: Increase version for release 2025-03-06 17:57:41 +01:00
Andrea Busi caf0e3b7cc feat: Add new minimal card in seismics list
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/87
2025-03-06 17:57:07 +01:00
Andrea Busi 4c35c38cc5 refactor: Move card informations to AppPreferences 2025-03-06 15:49:26 +01:00
Andrea Busi 521254c8c1 refactor: Remove unused constant 2025-03-06 13:01:20 +01:00
Andrea Busi 78a1710584 refactor: Replace deprecated methods 2025-03-06 10:59:36 +01:00
Andrea Busi b2a54a544c feat: Update layout in Seismic List section
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/86
2025-03-06 10:45:02 +01:00
Andrea Busi 0f5ad24744 feat: Update layout in Subscriptions section
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/85
2025-03-06 10:19:14 +01:00
Andrea Busi 0296cd50cd feat: Update layout in Reports section
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/84
2025-03-06 10:18:44 +01:00
Andrea Busi 7551988b4e feat: Update layout in Alerts section
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/83
2025-03-06 10:17:42 +01:00
Andrea Busi 5edcaaad99 feat: Change layout for base container card (center title) 2025-03-06 10:17:08 +01:00
29 changed files with 772 additions and 358 deletions
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 77;
objects = {
/* Begin PBXBuildFile section */
@@ -18,6 +18,8 @@
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 */; };
652247242D79DAA000D2B8DF /* SeismicNetworkMinimalTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652247232D79DAA000D2B8DF /* SeismicNetworkMinimalTableViewCell.swift */; };
652247262D79EC5E00D2B8DF /* SeismicNetworkBaseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652247252D79EC5E00D2B8DF /* SeismicNetworkBaseTableViewCell.swift */; };
6525A82625E13FD4008FE0D0 /* SeismicNetworkAdvertiseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6525A82525E13FD4008FE0D0 /* SeismicNetworkAdvertiseTableViewCell.swift */; };
652A3C6B2A8A757800719796 /* EQNBackgrounPositionDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652A3C6A2A8A757800719796 /* EQNBackgrounPositionDebugViewController.swift */; };
65355FFF25F38D3300BB57D2 /* SegnalazioniMapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65355FFE25F38D3300BB57D2 /* SegnalazioniMapViewController.swift */; };
@@ -320,6 +322,8 @@
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>"; };
652247232D79DAA000D2B8DF /* SeismicNetworkMinimalTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworkMinimalTableViewCell.swift; sourceTree = "<group>"; };
652247252D79EC5E00D2B8DF /* SeismicNetworkBaseTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworkBaseTableViewCell.swift; sourceTree = "<group>"; };
6525A82525E13FD4008FE0D0 /* SeismicNetworkAdvertiseTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworkAdvertiseTableViewCell.swift; sourceTree = "<group>"; };
652A3C6A2A8A757800719796 /* EQNBackgrounPositionDebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBackgrounPositionDebugViewController.swift; sourceTree = "<group>"; };
65355FFE25F38D3300BB57D2 /* SegnalazioniMapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegnalazioniMapViewController.swift; sourceTree = "<group>"; };
@@ -949,7 +953,9 @@
isa = PBXGroup;
children = (
65DBFB7D25E2CB020041CBA6 /* Ad templates */,
652247252D79EC5E00D2B8DF /* SeismicNetworkBaseTableViewCell.swift */,
DC28142F2519C24400C1AFF7 /* SeismicNetworkTableViewCell.swift */,
652247232D79DAA000D2B8DF /* SeismicNetworkMinimalTableViewCell.swift */,
6525A82525E13FD4008FE0D0 /* SeismicNetworkAdvertiseTableViewCell.swift */,
);
path = Cells;
@@ -1348,7 +1354,6 @@
};
};
buildConfigurationList = 8CBD3DBD2149B9AD0070C963 /* Build configuration list for PBXProject "Earthquake Network" */;
compatibilityVersion = "Xcode 8.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -1372,6 +1377,7 @@
65B16E282BDFB39B0020527E /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */,
65E6AC6C2C2DB3B60073F8FE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
);
preferredProjectObjectVersion = 77;
productRefGroup = 8CBD3DC32149B9AD0070C963 /* Products */;
projectDirPath = "";
projectReferences = (
@@ -1595,8 +1601,10 @@
650247122A61832F001AC512 /* EQNBackgroundPositionDebugHelper.swift in Sources */,
65CB83432915720400EE1E35 /* EQNUserData.swift in Sources */,
654D18C425F93C0600BB6DB0 /* PasquakesMapViewController.swift in Sources */,
652247242D79DAA000D2B8DF /* SeismicNetworkMinimalTableViewCell.swift in Sources */,
8C483CBC21FDACE500259FD2 /* EQNInAppProducts.swift in Sources */,
8C483CB821FDACD300259FD2 /* IAPHelper.swift in Sources */,
652247262D79EC5E00D2B8DF /* SeismicNetworkBaseTableViewCell.swift in Sources */,
6562C80725FFA6B100C85273 /* SeismicNetworkViewModel.swift in Sources */,
DCDE0BD924E58CCE00209778 /* EQNMainTabBarController.m in Sources */,
8C4E344B2152EE5B008B0D2A /* EQNGeneratoreURLServer.m in Sources */,
@@ -1810,7 +1818,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 146;
CURRENT_PROJECT_VERSION = 151;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = WJA4MR4CPC;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -1852,7 +1860,7 @@
CODE_SIGN_IDENTITY = "Apple Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 146;
CURRENT_PROJECT_VERSION = 151;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = WJA4MR4CPC;
GENERATE_INFOPLIST_FILE = YES;
@@ -2006,7 +2014,7 @@
CODE_SIGN_ENTITLEMENTS = "Earthquake Network/Earthquake Network.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 146;
CURRENT_PROJECT_VERSION = 151;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = WJA4MR4CPC;
@@ -2094,7 +2102,7 @@
CODE_SIGN_ENTITLEMENTS = "Earthquake Network/Earthquake Network.entitlements";
CODE_SIGN_IDENTITY = "Apple Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 146;
CURRENT_PROJECT_VERSION = 151;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = WJA4MR4CPC;
ENABLE_MODULE_VERIFIER = YES;
@@ -2179,7 +2187,7 @@
CODE_SIGN_ENTITLEMENTS = EQNNotificationContent/EQNNotificationContent.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 146;
CURRENT_PROJECT_VERSION = 151;
DEVELOPMENT_TEAM = WJA4MR4CPC;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -2213,7 +2221,7 @@
CODE_SIGN_ENTITLEMENTS = EQNNotificationContent/EQNNotificationContent.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 146;
CURRENT_PROJECT_VERSION = 151;
DEVELOPMENT_TEAM = WJA4MR4CPC;
GCC_PREPROCESSOR_DEFINITIONS = (
"ADS_ENABLED=0",
@@ -58,6 +58,10 @@ extension UserDefaults {
static let AlertsShowCardOptions = "EQNetwork.AlertsShowAllCards"
/// Indica lo stile di pin da visualizzare nelle mappe
static let MapPinStyle = "EQNetwork.MapPinStyle"
/// Indica le informazioni da visualizzare nelle card `small` e `full` nella Lista Sismi
static let SeismicNetworksCardInformations = "EQNetwork.SeismicInformations";
/// Indica la tipologia di card da visualizzare nella Lista Sismi
static let SeismicNetworksCardStyle = "EQNetwork.SeismicNetworksCardStyle"
// Migrazioni
static let AppMigrationV5_3 = "EQNUserDefaultMigrationV5_3"
@@ -100,8 +100,10 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
- (void)setupUI
{
self.title = [NSLocalizedString(@"tab_network", nil) capitalizedString];
self.tableView.estimatedRowHeight = 200.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.contentInset = EQNBaseContainerTableViewCell.EdgeInsets;
[self.tableView registerClass:[AlertsSmartphoneNetworkTableViewCell class] forCellReuseIdentifier:@"SmartphoneNetworkCell"];
[self.tableView registerClass:[AlertsPriorityServiceTableViewCell class] forCellReuseIdentifier:@"PriorityCell"];
[self.tableView registerClass:[AlertsNoLocationTableViewCell class] forCellReuseIdentifier:@"NoLocationCell"];
@@ -147,7 +149,7 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
[self.tableItems addObject:@(AllerteTableRowReteSmartphone)];
}
// check if locations is enabled
if (CLLocationManager.authorizationStatus != kCLAuthorizationStatusAuthorizedAlways) {
if (EQNUserData.sharedData.locationAuthorizationStatus != kCLAuthorizationStatusAuthorizedAlways) {
[self.tableItems addObject:@(AllerteTableRowLocationPermission)];
}
@@ -233,7 +235,7 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
- (void)actionTestPush
{
CLAuthorizationStatus status = CLLocationManager.authorizationStatus;
CLAuthorizationStatus status = EQNUserData.sharedData.locationAuthorizationStatus;
if (status != kCLAuthorizationStatusAuthorizedAlways && status != kCLAuthorizationStatusAuthorizedWhenInUse) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"attention", nil)
message:NSLocalizedString(@"liveview_unknown_location", nil)
@@ -271,7 +273,7 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
if (tableRow == AllerteTableRowLocationPermission) {
AlertsNoLocationTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"NoLocationCell" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell updateWith:CLLocationManager.authorizationStatus];
[cell updateWith:EQNUserData.sharedData.locationAuthorizationStatus];
return cell;
} else if (tableRow == AllerteTableRowSismiRilevati) {
@@ -7,7 +7,7 @@
//
import UIKit
import Shogun
@objc
class AlertsSmartphoneNetworkTableViewCell: EQNBaseContainerTableViewCell {
@@ -22,7 +22,7 @@ class AlertsSmartphoneNetworkTableViewCell: EQNBaseContainerTableViewCell {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.Colors.green
label.font = .preferredFont(forTextStyle: .largeTitle)
label.font = .preferredFont(forTextStyle: .largeTitle, weight: .bold)
label.textAlignment = .center
return label
}()
@@ -39,7 +39,7 @@ class SubscriptionDetailsViewController: UITableViewController {
products: [EQNInAppProducts]
) {
self.products = products
self.selectedProduct = products.first(where: { $0.plan == .monthly }) ?? products.first!
self.selectedProduct = products.first(where: { $0.plan == .yearly }) ?? products.first!
super.init(style: .plain)
}
@@ -67,6 +67,7 @@ class SubscriptionDetailsViewController: UITableViewController {
tableView.estimatedRowHeight = 2000.0
tableView.separatorStyle = .none
tableView.backgroundColor = .systemGroupedBackground
tableView.contentInset = EQNBaseContainerTableViewCell.EdgeInsets
tableView.registerCell(for: SubscriptionDetailsTableViewCell.self)
}
@@ -18,6 +18,7 @@ class SubscriptionsHeaderTableViewCell: UITableViewHeaderFooterView {
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.preferredFont(forTextStyle: .title2)
label.textColor = AppTheme.Colors.darkGray
label.textAlignment = .center
return label
}()
@@ -48,6 +49,7 @@ class SubscriptionsHeaderTableViewCell: UITableViewHeaderFooterView {
headerTitleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
headerTitleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: .cardPadding).isActive = true
headerTitleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: .cardPadding.negative).isActive = true
loadingActivityIndicator.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: .cardPadding.negative).isActive = true
loadingActivityIndicator.centerYAnchor.constraint(equalTo: headerTitleLabel.centerYAnchor).isActive = true
}
@@ -91,6 +91,11 @@ class SubscriptionsViewController: UITableViewController {
tableView.estimatedRowHeight = 600.0
tableView.separatorStyle = .none
tableView.backgroundColor = .systemGroupedBackground
tableView.contentInset = EQNBaseContainerTableViewCell.EdgeInsets
if #available(iOS 15.0, *) {
// remove extra padding on top of each section header
tableView.sectionHeaderTopPadding = 0.0
}
tableView.registerCell(for: SubscriptionsActiveTableViewCell.self)
tableView.registerCell(for: SubscriptionsDescriptionTableViewCell.self)
tableView.registerCell(for: SubscriptionProductTableViewCell.self)
@@ -190,17 +195,22 @@ class SubscriptionsViewController: UITableViewController {
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let tableSection = sections[section]
let view = tableView.dequeueHeaderFooterView(cellIdentifiable: SubscriptionsHeaderTableViewCell.self)
view.update(isLoading: isLoading, title: tableSection.sectionTitle)
return view
switch tableSection.sectionTitle {
case .some(let title):
let view = tableView.dequeueHeaderFooterView(cellIdentifiable: SubscriptionsHeaderTableViewCell.self)
view.update(isLoading: isLoading, title: title)
return view
case .none:
return nil
}
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
let tableSection = sections[section]
if tableSection.sectionTitle != nil {
return 50
return switch tableSection.sectionTitle {
case .some: 50.0
case .none: 0.0
}
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
@@ -16,7 +16,7 @@ class SegnalazioniLast24HoursCell: EQNBaseContainerTableViewCell {
@objc var onTapMap: (() -> Void)?
@objc var onTapTelegram: (() -> Void)?
override var headerText: String { NSLocalizedString("tab_manual", comment: "") }
override var isHeaderVisible: Bool { false }
// MARK: - UI
@@ -24,7 +24,7 @@ class SegnalazioniLast24HoursCell: EQNBaseContainerTableViewCell {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.Colors.red
label.font = .preferredFont(forTextStyle: .largeTitle)
label.font = .preferredFont(forTextStyle: .largeTitle, weight: .bold)
label.textAlignment = .center
label.numberOfLines = 0
return label
@@ -43,7 +43,7 @@ class SegnalazioniLast24HoursCell: EQNBaseContainerTableViewCell {
private lazy var twitterButton: UIButton = {
let button = EQNRoundedButton.make(target: self, action: #selector(twitterButtonTapped(_:)))
button.imageView?.contentMode = .scaleAspectFit
button.setImage(.init(named: "twitter_icon"), for: .normal)
button.setImage(.init(named: "xcorp_icon"), for: .normal)
return button
}()
@@ -38,8 +38,10 @@
- (void)setupUI
{
self.title = [NSLocalizedString(@"tab_manual", nil) capitalizedString];
self.tableView.estimatedRowHeight = 500.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.contentInset = EQNBaseContainerTableViewCell.EdgeInsets;
[self.tableView registerClass:[SegnalazioniLast24HoursCell class] forCellReuseIdentifier:@"Last24HCell"];
[self.tableView registerClass:[SegnalazioniSendReportCell class] forCellReuseIdentifier:@"ReportEarthquakeCell"];
}
@@ -0,0 +1,127 @@
//
// SeismicNetworkBaseTableViewCell.swift
// Earthquake Network
//
// Created by Andrea Busi on 06/03/25.
// Copyright © 2025 Earthquake Network. All rights reserved.
//
import UIKit
import Shogun
protocol SeismicNetworkBaseTableViewCellDelegate: AnyObject {
func seismicNetworkCellDidTapShare(_ cell: SeismicNetworkBaseTableViewCell)
func seismicNetworkCellDidTapMap(_ cell: SeismicNetworkBaseTableViewCell)
func seismicNetworkCellDidTapMapDetail(_ cell: SeismicNetworkBaseTableViewCell)
func seismicNetworkCellDidTapIntensityMapDetail(_ cell: SeismicNetworkBaseTableViewCell)
func seismicNetworkCellDidTapCalendar(_ cell: SeismicNetworkBaseTableViewCell)
func seismicNetworkCellDidTapSettings(_ cell: SeismicNetworkBaseTableViewCell)
func seismicNetworkCellDidTapClose(_ cell: SeismicNetworkBaseTableViewCell)
}
class SeismicNetworkBaseTableViewCell: UITableViewCell {
/// Delegate
weak var delegate: SeismicNetworkBaseTableViewCellDelegate?
/// Available informations to display inside the cell
enum InformationType: Int {
case preliminary
case time
case distance
case coordinate
case population
case realtimeSmartphones
case reportUsers
case intensityMap
case buttons
}
// MARL: - Internal
static let DefaultButtonHeight: CGFloat = 34.0
static let VerticalSpacingDefault: CGFloat = 6.0
static let VerticalSpacingSmall: CGFloat = 2.0
static let HorizontalSpacingDefault: CGFloat = 4.0
// MARK: - UI Components
lazy var containerView: UIView = {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.clipsToBounds = true
return view
}()
lazy var gradientView: UIImageView = {
// Per gestire il gradiente, utilizziamo una image view in cui inseriamo un'immagine
// creata ad-hoc con il gradiente desiderato.
// Le prove fatte utilizzando una view normale sono fallite perchè al momento di
// disegnare la view non abbiamo le misure corrette.
let view = UIImageView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.contentMode = .scaleToFill
return view
}()
// MARK: - Init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupUI()
}
// MARK: - View Lifecycle
override func layoutSubviews() {
super.layoutSubviews()
containerView.eqn_applyShadowAndRoundedCorners()
gradientView.eqn_applyRoundedCorners()
}
// MARK: - Setup
func setupUI() {
selectionStyle = .default
backgroundColor = .clear
// container view
contentView.addSubview(containerView)
containerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4.0).isActive = true
containerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8.0).isActive = true
containerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8.0).isActive = true
containerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -4.0).isActive = true
containerView.addSubview(gradientView)
gradientView.constraint(to: containerView)
}
func recreateUI() {
// remove all subviews and recreate the required components
containerView.subviews.forEach({ $0.removeFromSuperview() })
setupUI()
}
@discardableResult
func addSeparator(constraintTo: NSLayoutYAxisAnchor, constanst: CGFloat = 8.0) -> UIView {
let separator = UIView()
separator.translatesAutoresizingMaskIntoConstraints = false
separator.backgroundColor = .lightGray
containerView.addSubview(separator)
separator.topAnchor.constraint(equalTo: constraintTo, constant: constanst).isActive = true
separator.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
separator.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
separator.heightAnchor.constraint(equalToConstant: 1.0).isActive = true
return separator
}
}
@@ -0,0 +1,233 @@
//
// SeismicNetworkMinimalTableViewCell.swift
// Earthquake Network
//
// Created by Andrea Busi on 06/03/25.
// Copyright © 2025 Earthquake Network. All rights reserved.
//
import UIKit
import Shogun
class SeismicNetworkMinimalTableViewCell: SeismicNetworkBaseTableViewCell {
// MARK: - UI
private lazy var magnitudeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.preferredFont(forTextStyle: .largeTitle)
label.textColor = .red
label.textAlignment = .center
return label
}()
private lazy var placeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.preferredFont(forTextStyle: .title2, weight: .semibold)
label.numberOfLines = 3
return label
}()
private lazy var timeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .preferredFont(forTextStyle: .body)
label.numberOfLines = 2
return label
}()
private lazy var distanceLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .preferredFont(forTextStyle: .body)
label.numberOfLines = 2
return label
}()
private lazy var smartphonesLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.font = .preferredFont(forTextStyle: .body)
label.textAlignment = .center
label.numberOfLines = 2
return label
}()
private lazy var alertsLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.font = .preferredFont(forTextStyle: .body)
label.textAlignment = .center
label.numberOfLines = 2
return label
}()
// MARK: - Internal
/// Seismic to show
private var seismic: EQNSisma?
private var isPushSelected = false
private var informationTypes: Set<InformationType> = []
// MARK: - Setup
override func setupUI() {
super.setupUI()
// this variable is used to keep track of the previous view, in order to attach proper constraints
var previousView: UIView = containerView
// preliminary banner on top of the cell
if informationTypes.contains(.preliminary) {
let preliminaryLabel = UILabel()
preliminaryLabel.translatesAutoresizingMaskIntoConstraints = false
preliminaryLabel.text = NSLocalizedString("official_prelimiary", comment: "").uppercased()
preliminaryLabel.textAlignment = .center
preliminaryLabel.backgroundColor = .red
preliminaryLabel.textColor = .yellow
containerView.addSubview(preliminaryLabel)
preliminaryLabel.heightAnchor.constraint(equalToConstant: 30.0).isActive = true
preliminaryLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
preliminaryLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
preliminaryLabel.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
previousView = preliminaryLabel
}
containerView.addSubview(magnitudeLabel)
containerView.addSubview(placeLabel)
let titleTopAnchor = previousView == containerView ? containerView.layoutMarginsGuide.topAnchor : previousView.bottomAnchor
let stackViewInformations = UIStackView(arrangedSubviews: [timeLabel, distanceLabel])
stackViewInformations.translatesAutoresizingMaskIntoConstraints = false
stackViewInformations.axis = .horizontal
stackViewInformations.distribution = .fillEqually
stackViewInformations.spacing = Self.HorizontalSpacingDefault
containerView.addSubview(stackViewInformations)
let stackViewRight = UIStackView(arrangedSubviews: [placeLabel, stackViewInformations])
stackViewRight.translatesAutoresizingMaskIntoConstraints = false
stackViewRight.axis = .vertical
stackViewRight.distribution = .equalSpacing
stackViewRight.spacing = Self.VerticalSpacingDefault
let stackViewMain = UIStackView(arrangedSubviews: [magnitudeLabel, stackViewRight])
stackViewMain.translatesAutoresizingMaskIntoConstraints = false
stackViewMain.axis = .horizontal
stackViewMain.distribution = .fill
stackViewMain.spacing = Self.HorizontalSpacingDefault
containerView.addSubview(stackViewMain)
stackViewMain.topAnchor.constraint(equalTo: titleTopAnchor).isActive = true
stackViewMain.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
stackViewMain.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
magnitudeLabel.widthAnchor.constraint(equalToConstant: 60.0).isActive = true
previousView = stackViewMain
if informationTypes.contains(.realtimeSmartphones) || informationTypes.contains(.reportUsers) || informationTypes.contains(.intensityMap) {
let separator = addSeparator(constraintTo: previousView.bottomAnchor, constanst: Self.VerticalSpacingDefault)
let stackViewReports = UIStackView()
stackViewReports.translatesAutoresizingMaskIntoConstraints = false
stackViewReports.axis = .vertical
stackViewReports.distribution = .equalSpacing
stackViewReports.alignment = .center
stackViewReports.spacing = Self.VerticalSpacingDefault
if informationTypes.contains(.realtimeSmartphones) {
stackViewReports.addArrangedSubview(smartphonesLabel)
}
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: separator.bottomAnchor, constant: Self.VerticalSpacingDefault).isActive = true
stackViewReports.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
stackViewReports.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
previousView = stackViewReports
}
previousView.bottomAnchor.constraint(equalTo: containerView.layoutMarginsGuide.bottomAnchor).isActive = true
containerView.eqn_applyShadowAndRoundedCorners()
gradientView.eqn_applyRoundedCorners()
}
private func updateUI() {
guard let seismic = seismic else { return }
let viewModel = SeismicNetworkMinimalViewModel(seismic: seismic)
gradientView.image = .gradient(from: viewModel.colors.startColor, to: viewModel.colors.endColor, with: .init(origin: .zero, size: .init(width: 500, height: 1)))
placeLabel.text = viewModel.place
placeLabel.textColor = isPushSelected ? AppTheme.Colors.pureBlue : AppTheme.shared.cardTextColor
magnitudeLabel.textColor = viewModel.colors.textColor
magnitudeLabel.text = viewModel.magnitude
timeLabel.text = "🕗 \(viewModel.time)"
distanceLabel.text = "📐 \(viewModel.distance)"
if !viewModel.smartphones.isEmpty {
smartphonesLabel.text = "🚨 \(viewModel.smartphones)"
}
if !viewModel.users.isEmpty {
alertsLabel.text = "⚠️ \(viewModel.users)"
}
}
// MARK: - Public
/// Configure the cell to display a seismic
/// - Parameters:
/// - seismic: Seismic to display
/// - type: Type of cell
/// - informations: Informations to show
public func configure(
with seismic: EQNSisma,
isPushSelected: Bool
) {
self.seismic = seismic
self.isPushSelected = isPushSelected
self.informationTypes.removeAll()
if seismic.preliminary.intValue > 0 {
informationTypes.insert(.preliminary)
}
if seismic.smartphoneNumber.intValue > 0 {
informationTypes.insert(.realtimeSmartphones)
}
if seismic.userNumber.intValue > 0 {
informationTypes.insert(.reportUsers)
}
if seismic.isoCode != "0" {
informationTypes.insert(.intensityMap)
}
recreateUI()
updateUI()
}
// MARK: - Actions
@objc private func intensityMapTapped(_ sender: Any) {
delegate?.seismicNetworkCellDidTapIntensityMapDetail(self)
}
}
@@ -11,32 +11,8 @@ import MapKit
import CoreLocation
import Shogun
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)
}
class SeismicNetworkTableViewCell: UITableViewCell {
static let Identifier = "SeismicNetworkTableViewCell"
/// Available informations to display inside the cell
enum InformationType: Int {
case preliminary
case time
case distance
case coordinate
case population
case realtimeSmartphones
case reportUsers
case intensityMap
case buttons
}
class SeismicNetworkTableViewCell: SeismicNetworkBaseTableViewCell {
/// Available cell type
enum DisplayType {
@@ -45,14 +21,6 @@ class SeismicNetworkTableViewCell: UITableViewCell {
/// Cell with map visible
case mapExpanded
}
/// Delegate
weak var delegate: SeismicNetworkTableViewCellDelegate?
// MARK: - Internal
private static let DefaultButtonHeight: CGFloat = 30.0
private static let DefaultVerticalSpacing: CGFloat = 6.0
/// Seismic to show
private var seismic: EQNSisma?
@@ -61,26 +29,7 @@ class SeismicNetworkTableViewCell: UITableViewCell {
private var isPushSelected = false
// MARK: - UI Components
private lazy var containerView: UIView = {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.clipsToBounds = true
return view
}()
private lazy var gradientView: UIImageView = {
// Per gestire il gradiente, utilizziamo una image view in cui inseriamo un'immagine
// creata ad-hoc con il gradiente desiderato.
// Le prove fatte utilizzando una view normale sono fallite perchè al momento di
// disegnare la view non abbiamo le misure corrette.
let view = UIImageView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.contentMode = .scaleToFill
return view
}()
private lazy var placeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
@@ -89,6 +38,14 @@ class SeismicNetworkTableViewCell: UITableViewCell {
return label
}()
private lazy var shareButton: UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(UIImage(named: "share_icon"), for: .normal)
button.addTarget(self, action: #selector(shareTapped(_:)), for: .touchUpInside)
return button
}()
private lazy var networkLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
@@ -199,20 +156,9 @@ class SeismicNetworkTableViewCell: UITableViewCell {
// MARK: - Setup
private func setupUI() {
selectionStyle = .default
backgroundColor = .clear
// container view
contentView.addSubview(containerView)
containerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4.0).isActive = true
containerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8.0).isActive = true
containerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8.0).isActive = true
containerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -4.0).isActive = true
override func setupUI() {
super.setupUI()
containerView.addSubview(gradientView)
gradientView.constraint(to: containerView)
// this variable is used to keep track of the previous view, in order to attach proper constraints
var previousView: UIView = containerView
@@ -233,41 +179,27 @@ class SeismicNetworkTableViewCell: UITableViewCell {
previousView = preliminaryLabel
}
// title (bell icon, place label, seismic network and share button)
let titleComponentsHeight: CGFloat = 30.0
let stackViewTitle = UIStackView()
stackViewTitle.translatesAutoresizingMaskIntoConstraints = false
stackViewTitle.axis = .horizontal
stackViewTitle.distribution = .fill
stackViewTitle.alignment = .center
stackViewTitle.spacing = 4
let shareButton = UIButton(type: .custom)
shareButton.setImage(UIImage(named: "share_icon"), for: .normal)
shareButton.addTarget(self, action: #selector(shareTapped(_:)), for: .touchUpInside)
stackViewTitle.addArrangedSubview(placeLabel)
stackViewTitle.addArrangedSubview(shareButton)
placeLabel.setContentHuggingPriority(.init(200), for: .horizontal)
placeLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
shareButton.widthAnchor.constraint(equalToConstant: titleComponentsHeight).isActive = true
shareButton.widthAnchor.constraint(equalTo: shareButton.heightAnchor).isActive = true
containerView.addSubview(placeLabel)
containerView.addSubview(shareButton)
let titleTopAnchor = previousView == containerView ? containerView.layoutMarginsGuide.topAnchor : previousView.bottomAnchor
containerView.addSubview(stackViewTitle)
stackViewTitle.topAnchor.constraint(equalTo: titleTopAnchor).isActive = true
stackViewTitle.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
stackViewTitle.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
placeLabel.topAnchor.constraint(equalTo: titleTopAnchor).isActive = true
placeLabel.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
placeLabel.trailingAnchor.constraint(equalTo: shareButton.leadingAnchor, constant: .cardPadding.negative).isActive = true
shareButton.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
shareButton.centerYAnchor.constraint(equalTo: placeLabel.centerYAnchor).isActive = true
shareButton.heightAnchor.constraint(equalToConstant: 24.0).isActive = true
shareButton.heightAnchor.constraint(equalTo: shareButton.widthAnchor, multiplier: 1.0).isActive = true
let separator1 = addSeparator(constraintTo: stackViewTitle.bottomAnchor)
let separator1 = addSeparator(constraintTo: placeLabel.bottomAnchor)
let informationsLeadingAnchor = separator1.leadingAnchor
let informationsTrailingAnchor = separator1.trailingAnchor
// magnitude information
containerView.addSubview(magnitudeLabel)
magnitudeLabel.topAnchor.constraint(equalTo: separator1.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
magnitudeLabel.topAnchor.constraint(equalTo: separator1.bottomAnchor, constant: Self.VerticalSpacingSmall).isActive = true
magnitudeLabel.leadingAnchor.constraint(equalTo: informationsLeadingAnchor, constant: 14).isActive = true
if !informationTypes.contains(.preliminary) {
@@ -297,27 +229,27 @@ class SeismicNetworkTableViewCell: UITableViewCell {
}
containerView.addSubview(stackViewInformations)
stackViewInformations.topAnchor.constraint(equalTo: magnitudeLabel.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
stackViewInformations.topAnchor.constraint(equalTo: magnitudeLabel.bottomAnchor, constant: Self.VerticalSpacingSmall).isActive = true
stackViewInformations.leadingAnchor.constraint(equalTo: informationsLeadingAnchor, constant: 14).isActive = true
stackViewInformations.trailingAnchor.constraint(equalTo: informationsTrailingAnchor, constant: -14).isActive = true
previousView = stackViewInformations
// network
containerView.addSubview(networkLabel)
networkLabel.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
networkLabel.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.VerticalSpacingSmall).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 separator2 = addSeparator(constraintTo: previousView.bottomAnchor, constanst: Self.VerticalSpacingSmall)
let stackViewReports = UIStackView()
stackViewReports.translatesAutoresizingMaskIntoConstraints = false
stackViewReports.axis = .vertical
stackViewReports.distribution = .equalSpacing
stackViewReports.alignment = .center
stackViewReports.spacing = Self.DefaultVerticalSpacing
stackViewReports.spacing = Self.VerticalSpacingDefault
if informationTypes.contains(.realtimeSmartphones) {
stackViewReports.addArrangedSubview(smartphonesLabel)
@@ -334,15 +266,17 @@ class SeismicNetworkTableViewCell: UITableViewCell {
}
containerView.addSubview(stackViewReports)
stackViewReports.topAnchor.constraint(equalTo: separator2.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
stackViewReports.topAnchor.constraint(equalTo: separator2.bottomAnchor, constant: Self.VerticalSpacingDefault).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
previousView = stackViewReports
}
if informationTypes.contains(.buttons) {
let separator3 = addSeparator(constraintTo: previousView.bottomAnchor)
previousView = separator3
// buttons
let stackViewButtons = UIStackView()
stackViewButtons.translatesAutoresizingMaskIntoConstraints = false
@@ -359,7 +293,7 @@ class SeismicNetworkTableViewCell: UITableViewCell {
containerView.addSubview(stackViewButtons)
stackViewButtons.heightAnchor.constraint(equalToConstant: Self.DefaultButtonHeight).isActive = true
stackViewButtons.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
stackViewButtons.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.VerticalSpacingDefault).isActive = true
stackViewButtons.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
stackViewButtons.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
@@ -369,7 +303,7 @@ class SeismicNetworkTableViewCell: UITableViewCell {
if displayType == .mapExpanded {
containerView.addSubview(mapView)
mapView.heightAnchor.constraint(equalToConstant: 140.0).isActive = true
mapView.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
mapView.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.VerticalSpacingDefault).isActive = true
mapView.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
mapView.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
@@ -381,7 +315,7 @@ class SeismicNetworkTableViewCell: UITableViewCell {
containerView.addSubview(buttonClose)
buttonClose.heightAnchor.constraint(equalToConstant: Self.DefaultButtonHeight).isActive = true
buttonClose.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
buttonClose.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.VerticalSpacingDefault).isActive = true
buttonClose.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
buttonClose.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
buttonClose.bottomAnchor.constraint(equalTo: containerView.layoutMarginsGuide.bottomAnchor).isActive = true
@@ -394,12 +328,6 @@ class SeismicNetworkTableViewCell: UITableViewCell {
gradientView.eqn_applyRoundedCorners()
}
private func recreateUI() {
// remove all subviews and recreate the required components
containerView.subviews.forEach({ $0.removeFromSuperview() })
setupUI()
}
private func updateUI() {
guard let seismic = seismic else { return }
@@ -430,7 +358,6 @@ class SeismicNetworkTableViewCell: UITableViewCell {
alertsLabel.text = "⚠️ \(viewModel.users)"
}
if displayType == .mapExpanded {
// zoom based on population involved
let longitudeSpan = mapSpanLongitude(population: seismic.population100km)
@@ -491,52 +418,37 @@ class SeismicNetworkTableViewCell: UITableViewCell {
// MARK: - Actions
@objc func shareTapped(_ sender: UIButton) {
@objc private func shareTapped(_ sender: UIButton) {
delegate?.seismicNetworkCellDidTapShare(self)
}
@objc func mapTapped(_ sender: UIButton) {
@objc private func mapTapped(_ sender: UIButton) {
if displayType != .mapExpanded {
delegate?.seismicNetworkCellDidTapMap(self)
}
}
@objc func calendarTapped(_ sender: UIButton) {
@objc private func calendarTapped(_ sender: UIButton) {
delegate?.seismicNetworkCellDidTapCalendar(self)
}
@objc func settingsTapped(_ sender: UIButton) {
@objc private func settingsTapped(_ sender: UIButton) {
delegate?.seismicNetworkCellDidTapSettings(self)
}
@objc func closeTapped(_ sender: UIButton) {
@objc private func closeTapped(_ sender: UIButton) {
delegate?.seismicNetworkCellDidTapClose(self)
}
@objc func mapDetailTapped(_ sender: Any) {
@objc private func mapDetailTapped(_ sender: Any) {
delegate?.seismicNetworkCellDidTapMapDetail(self)
}
@objc func intensityMapTapped(_ sender: Any) {
@objc private func intensityMapTapped(_ sender: Any) {
delegate?.seismicNetworkCellDidTapIntensityMapDetail(self)
}
// MARK: - Helpers
@discardableResult
private func addSeparator(constraintTo: NSLayoutYAxisAnchor, constanst: CGFloat = 8.0) -> UIView {
let separator = UIView()
separator.translatesAutoresizingMaskIntoConstraints = false
separator.backgroundColor = .lightGray
containerView.addSubview(separator)
separator.topAnchor.constraint(equalTo: constraintTo, constant: constanst).isActive = true
separator.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
separator.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
separator.heightAnchor.constraint(equalToConstant: 1.0).isActive = true
return separator
}
/// Determines the zoom for the map, based on the involved population
private func mapSpanLongitude(population: Double) -> CLLocationDegrees {
@@ -29,17 +29,16 @@ class SeismicCardSettingsViewController: UIViewController {
@IBOutlet private weak var informationPopulationSwitch: UISwitch!
@IBOutlet private weak var closeButton: UIButton!
private var informations = [SeismicNetworkTableViewCell.InformationType]()
private var informations: [SeismicNetworkTableViewCell.InformationType] {
get { AppPreferences.shared.seismicNetworksInformations }
set { AppPreferences.shared.seismicNetworksInformations = newValue }
}
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
if let saved = UserDefaults.standard.array(forKey: EQNUserDefaultKeySesmicInformations) as? [Int] {
informations = saved.compactMap { SeismicNetworkTableViewCell.InformationType(rawValue: $0) }
}
setupUI()
updateUI()
}
@@ -84,7 +83,6 @@ class SeismicCardSettingsViewController: UIViewController {
toggle(information: .population)
}
UserDefaults.standard.set(informations.map { $0.rawValue }, forKey: EQNUserDefaultKeySesmicInformations)
updateUI()
}
@@ -47,7 +47,7 @@ class SeismicNetworkScrollIndicatorView: UIView {
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
// - usiamo un'altezza minima per il sisma evidenziato
// - per il sisma evidenziato, anche il contenuto è rosso (e non solo il bordo)
// - negli altri sismi, non mostriamo il bordo
@@ -74,9 +74,22 @@ class SeismicNetworkScrollIndicatorView: UIView {
// 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)
var offset: CGFloat = 0
if index > highlightIndex && smallRectangles {
// calcoliamo l'offset prima del rettangolo evidenziato
let preOffset = CGFloat(highlightIndex - 1) * rectStandardHeight
// offset diverso dovuto all'altezza diversa del rettangolo evidenziato
let highlightOffset = rectHighlightedMinHeight
// calcoliamo l'offset tra il rettangolo evidenziato e quello corrente
let postOffset = CGFloat(index - highlightIndex) * rectStandardHeight
offset = preOffset + highlightOffset + postOffset
} else {
// siamo prima del rettangolo evidenziato, non abbiamo calcoli da fare
offset = CGFloat(index) * rectHeight
}
let rectangle = CGRect(x: 0, y: offset, width: rectStandardWidth, height: rectHeight)
let fillColor = seismic.colors.textColor.withAlphaComponent(0.3)
context?.setFillColor(fillColor.cgColor)
@@ -87,8 +100,7 @@ class SeismicNetworkScrollIndicatorView: UIView {
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
context?.stroke(rectangle)
}
}
}
@@ -8,21 +8,62 @@
import Foundation
struct MagnitudeColors {
let textColor: UIColor
let startColor: UIColor
let endColor: UIColor
}
struct SeismicNetworkMinimalViewModel {
private let seismic: EQNSisma
let place: String
let isPreliminary: Bool
let magnitude: String
let time: String
let distance: String
let smartphones: String
let users: String
let colors: MagnitudeColors
// MARK: - Init
init(seismic: EQNSisma) {
self.seismic = seismic
self.place = seismic.place
let isPreliminary = seismic.preliminary.intValue > 0
self.isPreliminary = isPreliminary
self.magnitude = String(format: "%.1f", seismic.magnitude.doubleValue)
let time = EQNUtility.formattedString(forTimeDifference: Int(seismic.timeDifference))
self.time = time
let distanceRounded = Int(round(seismic.userDistance))
self.distance = "\(distanceRounded) km"
if seismic.smartphoneNumber.intValue > 0 {
self.smartphones = String(format: NSLocalizedString("official_smartphones", comment: ""), seismic.smartphoneNumber)
} else {
self.smartphones = ""
}
if seismic.userNumber.intValue > 0 {
self.users = String(format: NSLocalizedString("official_reports", comment: ""), seismic.userNumber)
} else {
self.users = ""
}
self.colors = calculateColors(for: seismic.magnitude.doubleValue)
}
}
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
let magnitude: String
let rawMagnitude: Double
let depth: String
let time: String
let distance: String
@@ -38,7 +79,6 @@ struct SeismicNetworkViewModel {
self.seismic = seismic
self.place = seismic.place
self.network = seismic.provider
self.rawMagnitude = seismic.magnitude.doubleValue
let isPreliminary = seismic.preliminary.intValue > 0
self.isPreliminary = isPreliminary
@@ -67,7 +107,7 @@ struct SeismicNetworkViewModel {
let coordinateText = EQNUtility.coordinateString(coordinate: seismic.coordinate.coordinate)
self.coordinate = "\(coordinateText)"
let population = Self.formatPopulation(seismic.population100km)
let population = formatPopulation(seismic.population100km)
self.population = String(format: NSLocalizedString("share_radius100", comment: ""), population)
if seismic.smartphoneNumber.intValue > 0 {
@@ -81,78 +121,8 @@ struct SeismicNetworkViewModel {
self.users = ""
}
self.colors = Self.calculateColors(for: seismic.magnitude.doubleValue)
self.colors = calculateColors(for: seismic.magnitude.doubleValue)
}
// MARK: - Private
/// Format population value (ex. 1.5M, 2.4k)
private static func formatPopulation(_ population: Double) -> String {
var populationString = ""
if population > 999_999 {
let roundedPopulation = round(population / 100_000) / 10
populationString = "\(roundedPopulation)M"
} else if population > 999 {
let roundedPopulation = round(population / 100) / 10
populationString = "\(roundedPopulation)K"
} else {
let roundedPopulation = round(population)
populationString = "\(roundedPopulation)"
}
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 {
@@ -160,3 +130,72 @@ extension SeismicNetworkViewModel: Equatable {
return lhs.seismic == rhs.seismic
}
}
// MARK: - Helpers
/// 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 .init(textColor: textColor, startColor: startColor, endColor: endColor)
}
/// Format population value (ex. 1.5M, 2.4k)
private func formatPopulation(_ population: Double) -> String {
var populationString = ""
if population > 999_999 {
let roundedPopulation = round(population / 100_000) / 10
populationString = "\(roundedPopulation)M"
} else if population > 999 {
let roundedPopulation = round(population / 100) / 10
populationString = "\(roundedPopulation)K"
} else {
let roundedPopulation = round(population)
populationString = "\(roundedPopulation)"
}
return populationString
}
@@ -21,6 +21,30 @@ class SeismicNetworksIntensityMapViewController: EQNBaseMapViewController {
override var isFilterViewVisible: Bool { false }
override var isCloseButtonVisible: Bool { false }
// MARK: - UI
lazy var descriptionView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = AppTheme.Colors.pureBlue
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: "")
view.addSubview(descriptionLabel)
descriptionLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 2.0).isActive = true
descriptionLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -2.0).isActive = true
descriptionLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 2.0).isActive = true
descriptionLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -2.0).isActive = true
return view
}()
// MARK: - Init
init(
@@ -39,27 +63,10 @@ class SeismicNetworksIntensityMapViewController: EQNBaseMapViewController {
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() {
@@ -138,7 +145,11 @@ class SeismicNetworksIntensityMapViewController: EQNBaseMapViewController {
}
private func shareScreenshot() {
let screenshot = createSnapshot()
let screenshot = createSnapshot(prepare: {
descriptionView.isHidden = true
}, restore: {
descriptionView.isHidden = false
})
let controller = UIActivityViewController(activityItems: [screenshot], applicationActivities: [])
present(controller, animated: true)
@@ -199,7 +210,11 @@ class SeismicNetworksIntensityMapViewController: EQNBaseMapViewController {
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 indexColor = if minIntensity == maxIntensity {
0
} else {
Int(round((intensity-minIntensity)/(maxIntensity-minIntensity)*255))
}
let lineColor = UIColor(hexString: shakemapColors[indexColor]) ?? .white
let textColor: UIColor = indexColor < 65 ? .white : .black
@@ -18,6 +18,12 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
case advertise(NativeAd)
}
enum CardDisplayType: Int, CaseIterable {
case small
case full
case minimal
}
private static let SegueIdentifierFilters = "ShowFilters"
private static let SegueIdentifierCardSettings = "ShowCardSettings"
@@ -34,9 +40,17 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
/// Cells to display (must be seismics or ad banners)
private var rows = [CellType]()
/// Type of cards to show
private var cardDisplayType: CardDisplayType {
get { AppPreferences.shared.seismicNetworksCardStyle }
set { AppPreferences.shared.seismicNetworksCardStyle = newValue }
}
private var seismicViewModels = [SeismicNetworkViewModel]()
/// Informations to display on a single cell
private var informations = [SeismicNetworkTableViewCell.InformationType]()
private var informations: [SeismicNetworkTableViewCell.InformationType] {
get { AppPreferences.shared.seismicNetworksInformations }
set { AppPreferences.shared.seismicNetworksInformations = newValue }
}
/// Index path of row with map expanded
private var openMapIndexPath: IndexPath?
/// Push notification opened by the user
@@ -53,7 +67,7 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
// MARK: - UI
@IBOutlet private weak var expandeCollapseButton: UIBarButtonItem!
@IBOutlet private weak var displayModeButton: UIBarButtonItem!
@IBOutlet private weak var sortButton: UIBarButtonItem!
private var tableViewTopConstraint: NSLayoutConstraint?
@@ -182,9 +196,11 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
tableView.estimatedRowHeight = 300.0
tableView.rowHeight = UITableView.automaticDimension
tableView.registerCell(for: SeismicNetworkTableViewCell.self)
tableView.registerCell(for: SeismicNetworkMinimalTableViewCell.self)
tableView.registerCell(for: SeismicNetworkAdvertiseTableViewCell.self)
tableView.emptyDataSetSource = self
tableView.separatorStyle = .none
tableView.contentInset = EQNBaseContainerTableViewCell.EdgeInsets
setupSortMenu()
}
@@ -259,14 +275,13 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
private func refreshUI() {
elaborateData()
if let saved = UserDefaults.standard.array(forKey: EQNUserDefaultKeySesmicInformations) as? [Int] {
informations = saved.compactMap { SeismicNetworkTableViewCell.InformationType(rawValue: $0) }
}
if informations.contains(.buttons) {
expandeCollapseButton.image = UIImage(named: "navbar-icon-arrow-collapse")
} else {
expandeCollapseButton.image = UIImage(named: "navbar-icon-arrow-expand")
switch cardDisplayType {
case .small:
displayModeButton.image = UIImage(systemName: "1.square")
case .full:
displayModeButton.image = UIImage(systemName: "2.square")
case .minimal:
displayModeButton.image = UIImage(systemName: "3.square")
}
tableView.reloadData()
@@ -631,13 +646,17 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
}
@IBAction func collapseExpandTapped(_ sender: Any) {
if informations.contains(.buttons) {
cardDisplayType.next()
switch cardDisplayType {
case .small:
informations.removeAll(where: { $0 == .buttons })
} else {
case .full:
informations.append(.buttons)
case .minimal:
break
}
UserDefaults.standard.set(informations.map { $0.rawValue }, forKey: EQNUserDefaultKeySesmicInformations)
refreshUI()
}
@@ -651,17 +670,26 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
let row = rows[indexPath.row]
switch row {
case .seismic(let seismic):
let cell = tableView.dequeueReusableCell(cellIdentifiable: SeismicNetworkTableViewCell.self, for: indexPath)
var type = SeismicNetworkTableViewCell.DisplayType.normal
if openMapIndexPath == indexPath {
type = .mapExpanded
switch cardDisplayType {
case .small, .full:
let cell = tableView.dequeueReusableCell(cellIdentifiable: SeismicNetworkTableViewCell.self, for: indexPath)
var type = SeismicNetworkTableViewCell.DisplayType.normal
if openMapIndexPath == indexPath {
type = .mapExpanded
}
let isPushSelected = isSeismicToHighlight(seismic: seismic)
cell.configure(with: seismic, type: type, informations: informations, isPushSelected: isPushSelected)
cell.delegate = self
return cell
case .minimal:
let cell = tableView.dequeueReusableCell(cellIdentifiable: SeismicNetworkMinimalTableViewCell.self, for: indexPath)
let isPushSelected = isSeismicToHighlight(seismic: seismic)
cell.configure(with: seismic, isPushSelected: isPushSelected)
cell.delegate = self
return cell
}
let isPushSelected = isSeismicToHighlight(seismic: seismic)
cell.configure(with: seismic, type: type, informations: informations, isPushSelected: isPushSelected)
cell.delegate = self
return cell
case .advertise(let nativeAd):
let cell = tableView.dequeueReusableCell(cellIdentifiable: SeismicNetworkAdvertiseTableViewCell.self, for: indexPath)
cell.loadNativeAd(nativeAd)
@@ -754,9 +782,9 @@ extension SeismicNetworksViewController: NativeAdLoaderDelegate {
}
}
extension SeismicNetworksViewController: SeismicNetworkTableViewCellDelegate {
extension SeismicNetworksViewController: SeismicNetworkBaseTableViewCellDelegate {
func seismicNetworkCellDidTapShare(_ cell: SeismicNetworkTableViewCell) {
func seismicNetworkCellDidTapShare(_ cell: SeismicNetworkBaseTableViewCell) {
guard let index = tableView.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
// create a snapshot of the cell and share with default share sheet
@@ -773,7 +801,7 @@ extension SeismicNetworksViewController: SeismicNetworkTableViewCellDelegate {
present(controller, animated: true)
}
func seismicNetworkCellDidTapMap(_ cell: SeismicNetworkTableViewCell) {
func seismicNetworkCellDidTapMap(_ cell: SeismicNetworkBaseTableViewCell) {
guard let index = tableView.indexPath(for: cell) else { return }
let indexToReloads = [openMapIndexPath, index].compactMap { $0 }
@@ -782,29 +810,29 @@ extension SeismicNetworksViewController: SeismicNetworkTableViewCellDelegate {
tableView.reloadRows(at: indexToReloads, with: .automatic)
}
func seismicNetworkCellDidTapMapDetail(_ cell: SeismicNetworkTableViewCell) {
func seismicNetworkCellDidTapMapDetail(_ cell: SeismicNetworkBaseTableViewCell) {
guard let index = tableView.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
showMapDetail(for: seismic)
}
func seismicNetworkCellDidTapIntensityMapDetail(_ cell: SeismicNetworkTableViewCell) {
func seismicNetworkCellDidTapIntensityMapDetail(_ cell: SeismicNetworkBaseTableViewCell) {
guard let index = tableView.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
showIntensityMap(for: seismic)
}
func seismicNetworkCellDidTapCalendar(_ cell: SeismicNetworkTableViewCell) {
func seismicNetworkCellDidTapCalendar(_ cell: SeismicNetworkBaseTableViewCell) {
guard let index = tableView.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
openCalendar(for: seismic)
}
func seismicNetworkCellDidTapSettings(_ cell: SeismicNetworkTableViewCell) {
func seismicNetworkCellDidTapSettings(_ cell: SeismicNetworkBaseTableViewCell) {
performSegue(withIdentifier: Self.SegueIdentifierCardSettings, sender: nil)
}
func seismicNetworkCellDidTapClose(_ cell: SeismicNetworkTableViewCell) {
func seismicNetworkCellDidTapClose(_ cell: SeismicNetworkBaseTableViewCell) {
guard let index = tableView.indexPath(for: cell) else { return }
let indexToReloads = [openMapIndexPath, index].compactMap { $0 }
-2
View File
@@ -64,8 +64,6 @@ static NSString * const EQNServerUrlAlertSimulator = @"https://srv.earthquakenet
#pragma mark - UserDefaults Keys
static NSString * const EQNUserDefaultKeySesmicInformations = @"EQNetwork.SeismicInformations";
static NSString * const EQNUserDefaultKeyOneShotShowCountry = @"EQNetwork.OneShot.CountrySelection";
static NSString * const EQNUserDefaultSeismicNetworkCards = @"EQNData.RetiSismiche";
#pragma mark - NSNotification
@@ -37,3 +37,13 @@ extension CGFloat {
self*2.0
}
}
extension CaseIterable where Self: Equatable {
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
}
}
@@ -39,4 +39,27 @@ class AppPreferences: NSObject {
UserDefaults.standard.set(newValue.rawValue, forKey: UserDefaults.MapPinStyle)
}
}
var seismicNetworksInformations: [SeismicNetworkTableViewCell.InformationType] {
get {
if let saved = UserDefaults.standard.array(forKey: UserDefaults.SeismicNetworksCardInformations) as? [Int] {
let informations = saved.compactMap { SeismicNetworkTableViewCell.InformationType(rawValue: $0) }
return informations
}
return [.buttons, .distance, .coordinate, .population, .intensityMap]
}
set {
UserDefaults.standard.set(newValue.map { $0.rawValue }, forKey: UserDefaults.SeismicNetworksCardInformations)
}
}
var seismicNetworksCardStyle: SeismicNetworksViewController.CardDisplayType {
get {
let saved = UserDefaults.standard.integer(forKey: UserDefaults.SeismicNetworksCardStyle)
return SeismicNetworksViewController.CardDisplayType(rawValue: saved) ?? .small
}
set {
UserDefaults.standard.set(newValue.rawValue, forKey: UserDefaults.SeismicNetworksCardStyle)
}
}
}
@@ -17,8 +17,6 @@ public class EQNUserDefaultsCommand: EQNCommandProtocol {
func execute() {
print("[EQNUserDefaultsCommand] Start execute")
applyDefaultSettings()
migrationV5_8()
migrationFirstAppStat()
migrationCriticalAlerts()
@@ -27,15 +25,6 @@ public class EQNUserDefaultsCommand: EQNCommandProtocol {
// MARK: - Private
private func applyDefaultSettings() {
// seismic card settings
if UserDefaults.standard.array(forKey: EQNUserDefaultKeySesmicInformations) == nil {
let informations: [SeismicNetworkTableViewCell.InformationType] = [.buttons, .distance, .coordinate, .population, .intensityMap]
UserDefaults.standard.set(informations.map { $0.rawValue }, forKey: EQNUserDefaultKeySesmicInformations)
}
}
private func migrationV5_8() {
let migrationPerformed = UserDefaults.standard.bool(forKey: UserDefaults.AppMigrationV5_8)
if migrationPerformed {
@@ -117,14 +106,16 @@ public class EQNUserDefaultsCommand: EQNCommandProtocol {
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)
// add new intensity map
var informations = AppPreferences.shared.seismicNetworksInformations
if !informations.contains(.intensityMap) {
informations.append(.intensityMap)
print("[EQNUserDefaultsCommand] Add intensityMap to seismic informations")
}
AppPreferences.shared.seismicNetworksInformations = informations
let cardDisplayType: SeismicNetworksViewController.CardDisplayType = informations.contains(.buttons) ? .full : .small
AppPreferences.shared.seismicNetworksCardStyle = cardDisplayType
userDefaults.set(true, forKey: UserDefaults.AppMigrationV5_9)
}
@@ -23,6 +23,13 @@ import CoreLocation
return !firstAppStartExecuted
}
// MARK: - Permissions
@objc
var locationAuthorizationStatus: CLAuthorizationStatus {
CLLocationManager().authorizationStatus
}
// MARK: - Firebase Token
@objc
@@ -12,12 +12,4 @@ 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
}
}
@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "twitter_icon.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "xcorp_icon.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
@@ -0,0 +1 @@
<svg id="vector" xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><path fill="#000000" d="M321.8,373.1h36.6L190,137.5H153.4ZM391,389.9H310.6L237,285.1 144.8,389.9H121L226.4,270 121,120h80.4l69.7,99.2L358.4,120h23.8L281.7,234.3Z" id="path_0"/></svg>

After

Width:  |  Height:  |  Size: 281 B

@@ -21,9 +21,9 @@
</view>
<navigationItem key="navigationItem" id="6kD-jk-5Aw">
<rightBarButtonItems>
<barButtonItem image="navbar-icon-refresh" id="ZJh-jF-ILm">
<barButtonItem image="1.square" catalog="system" id="HTN-07-s5p">
<connections>
<action selector="refreshDataTapped:" destination="tVM-DH-fmv" id="qs5-jS-0Op"/>
<action selector="collapseExpandTapped:" destination="tVM-DH-fmv" id="EnD-92-5ZX"/>
</connections>
</barButtonItem>
<barButtonItem image="navbar-icon-filters" id="vOM-Np-CIk">
@@ -32,15 +32,15 @@
</connections>
</barButtonItem>
<barButtonItem image="navbar-icon-sort" id="LyU-KI-3Mb"/>
<barButtonItem image="navbar-icon-arrow-collapse" id="HTN-07-s5p">
<barButtonItem image="navbar-icon-refresh" id="ZJh-jF-ILm">
<connections>
<action selector="collapseExpandTapped:" destination="tVM-DH-fmv" id="EnD-92-5ZX"/>
<action selector="refreshDataTapped:" destination="tVM-DH-fmv" id="qs5-jS-0Op"/>
</connections>
</barButtonItem>
</rightBarButtonItems>
</navigationItem>
<connections>
<outlet property="expandeCollapseButton" destination="HTN-07-s5p" id="lxP-Im-NME"/>
<outlet property="displayModeButton" destination="HTN-07-s5p" id="Lhc-Od-MvL"/>
<outlet property="sortButton" destination="LyU-KI-3Mb" id="969-Zg-YBB"/>
<segue destination="6LP-zk-O1z" kind="presentation" identifier="ShowFilters" modalPresentationStyle="overCurrentContext" modalTransitionStyle="crossDissolve" id="Nzu-iH-UgB"/>
<segue destination="Rfp-kt-2Kx" kind="presentation" identifier="ShowCardSettings" modalPresentationStyle="overCurrentContext" modalTransitionStyle="crossDissolve" id="VWw-16-xGw"/>
@@ -621,6 +621,7 @@
</scene>
</scenes>
<resources>
<image name="1.square" catalog="system" width="128" height="114"/>
<image name="navbar-icon-arrow-collapse" width="24" height="24"/>
<image name="navbar-icon-filters" width="24" height="24"/>
<image name="navbar-icon-refresh" width="24" height="24"/>
@@ -20,8 +20,12 @@ extension CGFloat {
static let cardVerticalSpacing: CGFloat = 10.0
}
@objc
class EQNBaseContainerTableViewCell: UITableViewCell {
/// Inset to apply to enclosing table view. Used to adjust spacing around the cells (for instance, top space is smaller that the space between the cards)
@objc static let EdgeInsets: UIEdgeInsets = .init(top: CGFloat.cardHorizontalMargin - CGFloat.cardVerticalMargin, left: 0, bottom: 0, right: 0)
// MARK: - UI
private lazy var internalContainerView: UIView = {
@@ -46,11 +50,11 @@ class EQNBaseContainerTableViewCell: UITableViewCell {
}()
private lazy var headerLabel: UILabel = {
let label = EQNEdgeInsetLabel()
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = AppTheme.Colors.lightBlue
label.font = .systemFont(ofSize: 17.0, weight: .medium)
label.textColor = .white
label.textAlignment = .center
label.font = .preferredFont(forTextStyle: .title2)
label.textColor = AppTheme.shared.cardTextColor
return label
}()
@@ -136,9 +140,9 @@ class EQNBaseContainerTableViewCell: UITableViewCell {
if isHeaderVisible {
containerView.addSubview(headerLabel)
headerLabel.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
headerLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: .cardPadding).isActive = true
headerLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
headerLabel.heightAnchor.constraint(equalToConstant: 26.0).isActive = true
headerLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
} else {
containerView.addSubview(emptyTopView)
emptyTopView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true