Files
eqn.ios/Sources/Earthquake Network/Controllers/Shared/EQNBaseMapViewController.swift
T

231 lines
7.9 KiB
Swift

//
// EQNBaseMapViewController.swift
// Earthquake Network
//
// Created by Andrea Busi on 06/03/21.
// Copyright © 2021 Earthquake Network. All rights reserved.
//
import Foundation
import MapKit
class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
var filter = EQNFiltroMappa.unGiorno {
didSet {
updateUI()
loadDataSource()
}
}
var availableFilters: [EQNFiltroMappa] {
[.unGiorno, .dodiciOre, .seiOre, .dueOre, .unOra, .dieciMinuti]
}
/// Annotations displayed on the map
private var mapAnnotations = [MKAnnotation]()
// MARK: - UI
lazy var closeButton: EQNBlurredCloseButton = {
let button = EQNBlurredCloseButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(closeTapped(_:)), for: .touchUpInside)
button.setTitleColor(.darkGray, for: .normal)
return button
}()
lazy var mapView: MKMapView = {
let mapView = MKMapView()
mapView.translatesAutoresizingMaskIntoConstraints = false
mapView.showsUserLocation = true
mapView.delegate = self
return mapView
}()
private lazy var containerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override var bannerContainerView: UIView? {
return containerView
}
private lazy var filtersView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .lightGray
// label with current selecte filter
view.addSubview(filterLabel)
filterLabel.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
filterLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
filterLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8.0).isActive = true
let imageView = UIImageView(image: UIImage(named: "icon-arrow-down"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.tintColor = .black
imageView.contentMode = .scaleAspectFit
view.addSubview(imageView)
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true
imageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 8.0).isActive = true
imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8.0).isActive = true
imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8.0).isActive = true
imageView.leadingAnchor.constraint(equalTo: filterLabel.trailingAnchor, constant: 8.0).isActive = true
// tap recognizer
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(filtersTapped(_:)))
view.addGestureRecognizer(tapRecognizer)
return view
}()
private lazy var filterLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = filter.title
return label
}()
// MARK: - Init
init() {
super.init(nibName: nil, bundle: nil)
setupUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupUI()
}
// MARK: - Private
private func setupUI() {
let isFilterVisible = !availableFilters.isEmpty
view.backgroundColor = .white
view.addSubview(mapView)
view.addSubview(closeButton)
view.addSubview(containerView)
if isFilterVisible {
view.addSubview(filtersView)
}
closeButton.addDefaultConstraint(to: view)
containerView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
bannerContainerHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: 55.0)
bannerContainerHeightConstraint?.isActive = true
if isFilterVisible {
view.backgroundColor = filtersView.backgroundColor // trick to simulate a bigger filters view
filtersView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
filtersView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
filtersView.bottomAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
filtersView.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
}
mapView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
mapView.bottomAnchor.constraint(equalTo: (isFilterVisible ? filtersView : containerView).topAnchor).isActive = true
}
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
registerMapAnnotationViews()
loadDataSource()
}
override func didReceiveDownloadComplete(_ notification: Notification) {
super.didReceiveDownloadComplete(notification)
// when data is download, reload data source
DispatchQueue.main.async {
self.loadDataSource()
}
}
// MARK: - Public
/// Load data to display on the map
func loadDataSource() {
// nope, subclass will implement some logic
}
/// Register annotation views for the current map
func registerMapAnnotationViews() {
// nope, subclass will implement some logic
}
/// Update the map with a set of annotations
func updateMap(with annotations: [MKAnnotation]) {
// remove previous annotations
mapView.removeAnnotations(mapAnnotations)
// set new annotations and reload map
mapAnnotations = annotations
mapView.addAnnotations(mapAnnotations)
mapView.showAnnotations(mapAnnotations, animated: true)
centerMap()
}
func centerMap() {
// nope, subclass will center map with it's own logic
}
// MARK: - Private
private func updateUI() {
filterLabel.text = filter.title
}
// MARK: - Actions
@objc func closeTapped(_ sender: Any) {
dismiss(animated: true)
}
@objc private func filtersTapped(_ sender: UIGestureRecognizer) {
let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
availableFilters.forEach { (filter) in
sheet.addAction(UIAlertAction(title: filter.title, style: .default, handler: { _ in
self.applyFilter(filter)
}))
}
sheet.addAction(UIAlertAction(title: NSLocalizedString("Annulla", comment: ""), style: .cancel, handler: nil))
present(sheet, animated: true, completion: nil)
}
private func applyFilter(_ filter: EQNFiltroMappa) {
self.filter = filter
}
// MARK: - MKMapViewDelegate
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard !annotation.isKind(of: MKUserLocation.self) else {
// Make a fast exit if the annotation is the `MKUserLocation`, as it's not an annotation view we wish to customize.
return nil
}
let annotationView = setupAnnotationView(for: annotation, on: mapView)
return annotationView
}
func setupAnnotationView(for annotation: MKAnnotation, on mapView: MKMapView) -> MKAnnotationView? {
return nil
}
}