169 lines
6.1 KiB
Swift
169 lines
6.1 KiB
Swift
//
|
|
// RealtimeAlertViewController.swift
|
|
// Earthquake Network
|
|
//
|
|
// Created by Andrea Busi on 15/06/22.
|
|
// Copyright © 2022 Earthquake Network. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import MapKit
|
|
|
|
|
|
class RealtimeAlertViewController: UIViewController, MKMapViewDelegate {
|
|
|
|
@objc var onClose: () -> Void = {}
|
|
|
|
// MARK: - Internal
|
|
|
|
private let notificationView = RealtimeAlertView()
|
|
/// Alert to display
|
|
private let realtimeAlert: EQNRealtimeAlert
|
|
/// Timer to constantly update countdown label
|
|
private var countdownTimer: Timer?
|
|
/// Refresh time for wave animation
|
|
private let waveAnimationRefreshRate = 0.1
|
|
/// Current radius of the wave animation on the map
|
|
private var waveAnimationCurrentRadius: CLLocationDistance = 0
|
|
private var waveAnimationVelocity: Double = 1_000
|
|
/// Timer to simulate animation for the wave
|
|
private var waveAnimationTimer: Timer?
|
|
|
|
// MARK: - Init
|
|
|
|
@objc
|
|
init(alert: EQNRealtimeAlert) {
|
|
self.realtimeAlert = alert
|
|
super.init(nibName: nil, bundle: nil)
|
|
|
|
self.waveAnimationCurrentRadius = currentWavePosition()
|
|
self.waveAnimationVelocity = evaluateWaveAnimationVelocity()
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
deinit {
|
|
// importante togliere il delegato, altrimenti causa crash
|
|
notificationView.mapView.delegate = nil
|
|
}
|
|
|
|
// MARK: - View Lifecycle
|
|
|
|
override func loadView() {
|
|
view = notificationView
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
configureUI()
|
|
updateUI()
|
|
|
|
startCountdown()
|
|
startWaveAnimation()
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
private func configureUI() {
|
|
notificationView.closeButton.addTarget(self, action: #selector(onTapClose(_:)), for: .touchUpInside)
|
|
}
|
|
|
|
private func updateUI() {
|
|
notificationView.descriptionLabel.text = realtimeAlert.title
|
|
|
|
// update title with distance from earthquake
|
|
let distanceRound = Int(round(realtimeAlert.distanceFromUser() / 1_000))
|
|
notificationView.descriptionLabel.text = (notificationView.descriptionLabel.text ?? "")
|
|
+ ".\n"
|
|
+ String(format: NSLocalizedString("timer_message2_other", comment: ""), distanceRound)
|
|
|
|
// center map on the earthquake coordinate
|
|
let span = MKCoordinateSpan(latitudeDelta: 10.5, longitudeDelta: 10.5)
|
|
let region = MKCoordinateRegion(center: realtimeAlert.coordinate.coordinate, span: span)
|
|
notificationView.mapView.setCenter(realtimeAlert.coordinate.coordinate, animated: false)
|
|
notificationView.mapView.setRegion(region, animated: true)
|
|
|
|
// aggiungiamo annotation con epicentro sisma
|
|
notificationView.addMapAnnotation(center: realtimeAlert.coordinate.coordinate, intensity: realtimeAlert.intensity)
|
|
|
|
// simuliamo animazione dell'onda sismica
|
|
notificationView.addMapCircle(center: realtimeAlert.coordinate.coordinate, radius: waveAnimationCurrentRadius, overlayId: "wave_animation")
|
|
|
|
// aggiungiamo un segmento tra la posizione del sisma e quella dell'utente
|
|
if let lastPosition = EQNUser.default().lastPosition {
|
|
notificationView.addMapLine(coordinates: [realtimeAlert.coordinate.coordinate, lastPosition.coordinate])
|
|
}
|
|
}
|
|
|
|
private func startCountdown() {
|
|
// show countdown only if time is less than 300 seconds
|
|
if realtimeAlert.currentCountdown() < 300 {
|
|
// start a timer for the countdown label
|
|
notificationView.waveTimeLabel.isHidden = false
|
|
countdownTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(countdownTimerFired(_:)), userInfo: nil, repeats: true)
|
|
countdownTimer?.fire()
|
|
}
|
|
}
|
|
|
|
private func startWaveAnimation() {
|
|
waveAnimationTimer = Timer.scheduledTimer(timeInterval: waveAnimationRefreshRate, target: self, selector: #selector(mapWaveAnimationFired(_:)), userInfo: nil, repeats: true)
|
|
waveAnimationTimer?.fire()
|
|
}
|
|
|
|
// MARK: - Action
|
|
|
|
@objc private func onTapClose(_ sender: UIButton) {
|
|
// invalidiamo i timer, altri
|
|
countdownTimer?.invalidate()
|
|
countdownTimer = nil
|
|
waveAnimationTimer?.invalidate()
|
|
waveAnimationTimer = nil
|
|
|
|
onClose()
|
|
dismiss(animated: true)
|
|
}
|
|
|
|
|
|
// MARK: - Timer
|
|
|
|
@objc private func countdownTimerFired(_ sender: Timer) {
|
|
let countdown = realtimeAlert.currentCountdown()
|
|
notificationView.waveTimeLabel.text = String(format: NSLocalizedString("alert_wave", comment: ""), countdown)
|
|
|
|
if countdown <= 0 {
|
|
// stop the countdown
|
|
countdownTimer?.invalidate()
|
|
countdownTimer = nil
|
|
}
|
|
}
|
|
|
|
@objc private func mapWaveAnimationFired(_ sender: Timer) {
|
|
waveAnimationCurrentRadius += waveAnimationVelocity
|
|
notificationView.addMapCircle(center: realtimeAlert.coordinate.coordinate, radius: waveAnimationCurrentRadius, overlayId: "wave_animation")
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
/// Evaluate current position for the wave
|
|
/// Used to define initial position for the wave circle
|
|
/// - Returns: Distance of the wave from the original earthquake point
|
|
private func currentWavePosition() -> Double {
|
|
// distanza tra utente e terremoto
|
|
let distance = realtimeAlert.distanceFromUser()
|
|
|
|
// calcoliamo la distanza rimanente da mostrare, perchè la schermata potrebbe anche essere aperta in ritardo
|
|
let remainingDistance = realtimeAlert.waveSpeed * Double(realtimeAlert.currentCountdown())
|
|
return distance - remainingDistance
|
|
}
|
|
|
|
/// Evaluate wave velocity based on push notification data
|
|
/// - Returns: Wave velocity, used for animation
|
|
private func evaluateWaveAnimationVelocity() -> Double {
|
|
let velocity = realtimeAlert.waveSpeed
|
|
return velocity * waveAnimationRefreshRate
|
|
}
|
|
}
|