608 lines
26 KiB
Swift
608 lines
26 KiB
Swift
//
|
|
// SeismicNetworkTableViewCell.swift
|
|
// Earthquake Network
|
|
//
|
|
// Created by Busi Andrea on 22/09/2020.
|
|
// Copyright © 2020 Earthquake Network. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import MapKit
|
|
import CoreLocation
|
|
import Shogun
|
|
|
|
protocol SeismicNetworkTableViewCellDelegate: AnyObject {
|
|
func seismicNetworkCellDidTapShare(_ cell: SeismicNetworkTableViewCell)
|
|
func seismicNetworkCellDidTapMap(_ cell: SeismicNetworkTableViewCell)
|
|
func seismicNetworkCellDidTapMapDetail(_ cell: SeismicNetworkTableViewCell)
|
|
func seismicNetworkCellDidTapCalendar(_ cell: SeismicNetworkTableViewCell)
|
|
func seismicNetworkCellDidTapSettings(_ cell: SeismicNetworkTableViewCell)
|
|
func seismicNetworkCellDidTapClose(_ cell: SeismicNetworkTableViewCell)
|
|
}
|
|
|
|
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
|
|
case time
|
|
case distance
|
|
case coordinate
|
|
case population
|
|
case realtimeSmartphones
|
|
case reportUsers
|
|
case buttons
|
|
}
|
|
|
|
/// Available cell type
|
|
enum DisplayType {
|
|
/// Compact view
|
|
case normal
|
|
/// Cell with map visible
|
|
case mapExpanded
|
|
}
|
|
|
|
/// Delegate
|
|
weak var delegate: SeismicNetworkTableViewCellDelegate?
|
|
|
|
// MARK: - Internal
|
|
|
|
private static let DefaultVerticalSpacing: CGFloat = 6.0
|
|
private static let DefaultBodyFont = UIFont.preferredFont(forTextStyle: .body)
|
|
private static let DefaultBodyFontLight = UIFont.preferredFont(forTextStyle: .body, weight: .light)
|
|
|
|
/// Seismic to show
|
|
private var seismic: EQNSisma?
|
|
private(set) var displayType = DisplayType.normal
|
|
private var informationTypes = [InformationType]()
|
|
|
|
private var colors: MagnitudeColors?
|
|
|
|
// MARK: - UI Components
|
|
|
|
private lazy var containerView: UIView = {
|
|
let view = UIView(frame: .zero)
|
|
view.translatesAutoresizingMaskIntoConstraints = false
|
|
view.layer.cornerRadius = AppTheme.shared.cardCornerRadius
|
|
view.layer.masksToBounds = false
|
|
|
|
// add shadow
|
|
view.layer.shadowColor = UIColor.black.cgColor
|
|
view.layer.shadowOpacity = 0.5
|
|
view.layer.shadowOffset = CGSize(width: 0, height: 2)
|
|
view.layer.shadowRadius = 2
|
|
return view
|
|
}()
|
|
|
|
private lazy var titleImageView: UIImageView = {
|
|
let imageView = UIImageView(frame: .zero)
|
|
imageView.translatesAutoresizingMaskIntoConstraints = false
|
|
return imageView
|
|
}()
|
|
|
|
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 networkLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
label.backgroundColor = UIColor.white.withAlphaComponent(0.5)
|
|
label.textAlignment = .center
|
|
return label
|
|
}()
|
|
|
|
private lazy var magnitudeLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
label.font = UIFont.preferredFont(forTextStyle: .largeTitle)
|
|
label.textColor = .red
|
|
return label
|
|
}()
|
|
|
|
private lazy var depthLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
label.font = Self.DefaultBodyFontLight
|
|
return label
|
|
}()
|
|
|
|
private lazy var timeLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
label.font = Self.DefaultBodyFontLight
|
|
return label
|
|
}()
|
|
|
|
private lazy var distanceLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
label.font = Self.DefaultBodyFontLight
|
|
return label
|
|
}()
|
|
|
|
private lazy var coordinateLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
label.font = Self.DefaultBodyFontLight
|
|
return label
|
|
}()
|
|
|
|
private lazy var populationLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
label.font = Self.DefaultBodyFontLight
|
|
return label
|
|
}()
|
|
|
|
private lazy var smartphonesLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
label.numberOfLines = 0
|
|
label.font = Self.DefaultBodyFont
|
|
label.textAlignment = .center
|
|
return label
|
|
}()
|
|
|
|
private lazy var alertsLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
label.numberOfLines = 0
|
|
label.font = Self.DefaultBodyFont
|
|
label.textAlignment = .center
|
|
return label
|
|
}()
|
|
|
|
private lazy var mapView: MKMapView = {
|
|
let mapView = MKMapView(frame: .zero)
|
|
mapView.translatesAutoresizingMaskIntoConstraints = false
|
|
mapView.isScrollEnabled = false
|
|
mapView.isZoomEnabled = false
|
|
mapView.layer.cornerRadius = AppTheme.shared.cardCornerRadius
|
|
mapView.layer.masksToBounds = true
|
|
return mapView
|
|
}()
|
|
|
|
// 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: - 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
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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(titleImageView)
|
|
stackViewTitle.addArrangedSubview(placeLabel)
|
|
stackViewTitle.addArrangedSubview(networkLabel)
|
|
stackViewTitle.addArrangedSubview(shareButton)
|
|
|
|
titleImageView.heightAnchor.constraint(equalToConstant: titleComponentsHeight).isActive = true
|
|
titleImageView.widthAnchor.constraint(equalTo: titleImageView.heightAnchor).isActive = true
|
|
networkLabel.heightAnchor.constraint(equalToConstant: 34.0).isActive = true
|
|
networkLabel.setContentHuggingPriority(.init(800), for: .horizontal)
|
|
networkLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
|
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
|
|
|
|
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
|
|
|
|
let separator1 = addSeparator(constraintTo: stackViewTitle.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.leadingAnchor.constraint(equalTo: informationsLeadingAnchor, constant: 14).isActive = true
|
|
|
|
if !informationTypes.contains(.preliminary) {
|
|
containerView.addSubview(depthLabel)
|
|
depthLabel.lastBaselineAnchor.constraint(equalTo: magnitudeLabel.lastBaselineAnchor).isActive = true
|
|
depthLabel.leadingAnchor.constraint(equalTo: magnitudeLabel.trailingAnchor, constant: 16).isActive = true
|
|
}
|
|
|
|
// informations
|
|
let stackViewInformations = UIStackView()
|
|
stackViewInformations.translatesAutoresizingMaskIntoConstraints = false
|
|
stackViewInformations.axis = .vertical
|
|
stackViewInformations.distribution = .equalSpacing
|
|
stackViewInformations.spacing = 4
|
|
|
|
if informationTypes.contains(.time) {
|
|
stackViewInformations.addArrangedSubview(timeLabel)
|
|
}
|
|
if informationTypes.contains(.distance) {
|
|
stackViewInformations.addArrangedSubview(distanceLabel)
|
|
}
|
|
if informationTypes.contains(.coordinate) {
|
|
stackViewInformations.addArrangedSubview(coordinateLabel)
|
|
}
|
|
if informationTypes.contains(.population) {
|
|
stackViewInformations.addArrangedSubview(populationLabel)
|
|
}
|
|
|
|
containerView.addSubview(stackViewInformations)
|
|
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)
|
|
|
|
let stackViewReports = UIStackView()
|
|
stackViewReports.translatesAutoresizingMaskIntoConstraints = false
|
|
stackViewReports.axis = .vertical
|
|
stackViewReports.distribution = .equalSpacing
|
|
stackViewReports.alignment = .center
|
|
stackViewReports.spacing = Self.DefaultVerticalSpacing
|
|
|
|
if informationTypes.contains(.realtimeSmartphones) {
|
|
stackViewReports.addArrangedSubview(smartphonesLabel)
|
|
}
|
|
if informationTypes.contains(.reportUsers) {
|
|
stackViewReports.addArrangedSubview(alertsLabel)
|
|
}
|
|
|
|
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
|
|
|
|
let separator3 = addSeparator(constraintTo: stackViewReports.bottomAnchor)
|
|
previousView = separator3
|
|
}
|
|
|
|
if informationTypes.contains(.buttons) {
|
|
// buttons
|
|
let stackViewButtons = UIStackView()
|
|
stackViewButtons.translatesAutoresizingMaskIntoConstraints = false
|
|
stackViewButtons.axis = .horizontal
|
|
stackViewButtons.distribution = .fillEqually
|
|
stackViewButtons.spacing = 4
|
|
|
|
let buttonMap = createRoundedButton(title: "🗺", action: #selector(mapTapped(_:)))
|
|
stackViewButtons.addArrangedSubview(buttonMap)
|
|
let buttonCalendar = createRoundedButton(title: "📆", action: #selector(calendarTapped(_:)))
|
|
stackViewButtons.addArrangedSubview(buttonCalendar)
|
|
let buttonSettings = createRoundedButton(title: "🔧", action: #selector(settingsTapped(_:)))
|
|
stackViewButtons.addArrangedSubview(buttonSettings)
|
|
|
|
containerView.addSubview(stackViewButtons)
|
|
stackViewButtons.heightAnchor.constraint(equalToConstant: 30.0).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
|
|
|
|
previousView = stackViewButtons
|
|
}
|
|
|
|
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.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
|
|
mapView.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
|
|
|
|
previousView = mapView
|
|
}
|
|
|
|
if (displayType == .mapExpanded) {
|
|
let buttonClose = createRoundedButton(title: NSLocalizedString("official_close", comment: "").uppercased(), action: #selector(closeTapped(_:)))
|
|
|
|
containerView.addSubview(buttonClose)
|
|
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
|
|
buttonClose.bottomAnchor.constraint(equalTo: containerView.layoutMarginsGuide.bottomAnchor).isActive = true
|
|
}
|
|
else {
|
|
previousView.bottomAnchor.constraint(equalTo: containerView.layoutMarginsGuide.bottomAnchor).isActive = true
|
|
}
|
|
}
|
|
|
|
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 }
|
|
|
|
let viewModel = SeismicNetworkViewModel(seismic: seismic)
|
|
|
|
containerView.backgroundColor = colors?.startColor
|
|
|
|
let notified = couldBeNotified(for: seismic)
|
|
titleImageView.image = notified ? UIImage(named: "bell") : UIImage(named: "bell_disabled")
|
|
|
|
// update seismic data
|
|
placeLabel.text = viewModel.place
|
|
networkLabel.text = viewModel.network + " " // add some padding
|
|
magnitudeLabel.textColor = colors?.textColor
|
|
magnitudeLabel.text = viewModel.magnitude
|
|
depthLabel.text = viewModel.depth
|
|
timeLabel.text = "🕗 \(viewModel.time)"
|
|
distanceLabel.text = "📐 \(viewModel.distance)"
|
|
coordinateLabel.text = "🌍 \(viewModel.coordinate)"
|
|
|
|
// evaluate population string
|
|
populationLabel.text = "👥 \(viewModel.population)"
|
|
let populationIsRed = seismic.population100km >= 1_000_000 || seismic.userDistance <= 250
|
|
populationLabel.textColor = populationIsRed ? AppTheme.Colors.red : .black
|
|
|
|
if !viewModel.smartphones.isEmpty {
|
|
smartphonesLabel.text = "🚨 \(viewModel.smartphones)"
|
|
}
|
|
if !viewModel.users.isEmpty {
|
|
alertsLabel.text = "⚠️ \(viewModel.users)"
|
|
}
|
|
|
|
|
|
if displayType == .mapExpanded {
|
|
// zoom based on population involved
|
|
let longitudeSpan = mapSpanLongitude(population: seismic.population100km)
|
|
let span = MKCoordinateSpan(latitudeDelta: longitudeSpan * 1.2, longitudeDelta: longitudeSpan)
|
|
let region = MKCoordinateRegion(center: seismic.coordinate.coordinate, span: span)
|
|
mapView.setCenter(seismic.coordinate.coordinate, animated: false)
|
|
mapView.setRegion(region, animated: false)
|
|
|
|
// add a pin on the center
|
|
let annotation = MKPointAnnotation()
|
|
annotation.coordinate = seismic.coordinate.coordinate
|
|
annotation.title = ""
|
|
mapView.addAnnotation(annotation)
|
|
|
|
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(mapDetailTapped(_:)))
|
|
mapView.addGestureRecognizer(tapRecognizer)
|
|
}
|
|
}
|
|
|
|
// 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, type: DisplayType, informations: [InformationType]) {
|
|
self.seismic = seismic
|
|
self.colors = calculateColors(for: seismic.magnitude.doubleValue)
|
|
self.displayType = type
|
|
self.informationTypes = informations
|
|
|
|
if !informations.contains(.time) {
|
|
self.informationTypes += [.time]
|
|
}
|
|
|
|
if seismic.preliminary.intValue > 0 && !informations.contains(.preliminary) {
|
|
self.informationTypes += [.preliminary]
|
|
}
|
|
if seismic.smartphoneNumber.intValue > 0 && !informations.contains(.realtimeSmartphones) {
|
|
self.informationTypes += [.realtimeSmartphones]
|
|
}
|
|
if seismic.userNumber.intValue > 0 && !informations.contains(.reportUsers) {
|
|
self.informationTypes += [.reportUsers]
|
|
}
|
|
|
|
recreateUI()
|
|
updateUI()
|
|
}
|
|
|
|
// MARK: - Actions
|
|
|
|
@objc func shareTapped(_ sender: UIButton) {
|
|
delegate?.seismicNetworkCellDidTapShare(self)
|
|
}
|
|
|
|
@objc func mapTapped(_ sender: UIButton) {
|
|
if displayType != .mapExpanded {
|
|
delegate?.seismicNetworkCellDidTapMap(self)
|
|
}
|
|
}
|
|
|
|
@objc func calendarTapped(_ sender: UIButton) {
|
|
delegate?.seismicNetworkCellDidTapCalendar(self)
|
|
}
|
|
|
|
@objc func settingsTapped(_ sender: UIButton) {
|
|
delegate?.seismicNetworkCellDidTapSettings(self)
|
|
}
|
|
|
|
@objc func closeTapped(_ sender: UIButton) {
|
|
delegate?.seismicNetworkCellDidTapClose(self)
|
|
}
|
|
|
|
@objc func mapDetailTapped(_ sender: Any) {
|
|
delegate?.seismicNetworkCellDidTapMapDetail(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
|
|
}
|
|
|
|
private func createRoundedButton(title: String, action: Selector) -> EQNRoundedButton {
|
|
let button = EQNRoundedButton(frame: .zero)
|
|
button.translatesAutoresizingMaskIntoConstraints = false
|
|
button.addTarget(self, action: action, for: .touchUpInside)
|
|
button.setTitle(title, for: .normal)
|
|
button.setTitleColor(AppTheme.Colors.darkGray, for: .normal)
|
|
button.backgroundColor = UIColor.white.withAlphaComponent(0.5)
|
|
return button
|
|
}
|
|
|
|
/// Check if the user could be received a notification for this seismic
|
|
private func couldBeNotified(for seismic: EQNSisma) -> Bool {
|
|
let settings = EQNNotificheReteSismiche.shared()
|
|
|
|
if !settings.isAbilitato {
|
|
return false
|
|
}
|
|
|
|
if !settings.listaEnti.contains(seismic.provider) {
|
|
return false
|
|
}
|
|
|
|
var notified = true
|
|
if let radius = Double(settings.distanzaPosizione), seismic.userDistance > radius {
|
|
notified = false
|
|
}
|
|
if let magnitude = Double(settings.energiaSisma), seismic.magnitude.doubleValue < magnitude {
|
|
notified = false
|
|
}
|
|
|
|
if settings.isAbilitaVicini, seismic.userDistance < 50 {
|
|
notified = true
|
|
}
|
|
|
|
if settings.isTerremortiForti, let strongMagnitude = Double(settings.energiaTerremotiForti), seismic.magnitude.doubleValue >= strongMagnitude {
|
|
notified = true
|
|
}
|
|
|
|
return notified
|
|
}
|
|
|
|
/// Determines the zoom for the map, based on the involved population
|
|
private func mapSpanLongitude(population: Double) -> CLLocationDegrees {
|
|
var zoom: CLLocationDegrees = 1
|
|
if population > 1_000_000 {
|
|
zoom = 1
|
|
} else if population < 500 {
|
|
zoom = 24
|
|
} else {
|
|
zoom = 6
|
|
}
|
|
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)
|
|
}
|
|
}
|