439 lines
14 KiB
Swift
439 lines
14 KiB
Swift
//
|
|
// MapsViewController.swift
|
|
// PatternsSwift
|
|
//
|
|
// Created by mrustaa on 02/05/2020.
|
|
// Copyright © 2020 mrustaa. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import MapKit
|
|
import CoreLocation
|
|
import ContainerControllerSwift
|
|
|
|
class MapsViewController: StoryboardController, MapsContainerControllerDelegate, LocationContainerControllerDelegate, RouteContainerControllerDelegate, MenuContainerControllerDelegate {
|
|
|
|
// MARK: - IBOutlets
|
|
|
|
@IBOutlet weak var mapView: MKMapView!
|
|
|
|
@IBOutlet weak var mapWeatherView: MapsWeatherView!
|
|
|
|
@IBOutlet weak var mapButtons: MapsButtons!
|
|
@IBOutlet weak var mapButtonsPaddingTop: NSLayoutConstraint!
|
|
@IBOutlet weak var mapButtonsPaddingRight: NSLayoutConstraint!
|
|
|
|
@IBOutlet weak var statusBlur: UIVisualEffectView!
|
|
@IBOutlet weak var statusBarBlurHeight: NSLayoutConstraint!
|
|
|
|
// MARK: - Properties
|
|
|
|
var mapManager: MapViewManager!
|
|
|
|
var mapsContainer: MapsContainerController!
|
|
var locationContainer: LocationContainerController?
|
|
var routeContainer: RouteContainerController?
|
|
var menuContainer: MenuContainerController!
|
|
|
|
var darkStyle: Bool = false
|
|
var selectedIndex: Int = 0
|
|
var showOnce: Bool = false
|
|
|
|
// MARK: - Life Cycle
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
|
|
|
|
updateMapManager()
|
|
|
|
showMapsContainer()
|
|
|
|
updateMapViewButtons()
|
|
updateMapViewWeatherView()
|
|
updateMapViewTopPadding()
|
|
startAnimationMapElements()
|
|
}
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
|
|
setNeedsStatusBarAppearanceUpdate()
|
|
|
|
navBar(hide: true)
|
|
if !showOnce {
|
|
mapsContainer.move(type: .hide, animation: false)
|
|
}
|
|
}
|
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
super.viewDidAppear(animated)
|
|
|
|
if !showOnce {
|
|
showOnce = true
|
|
mapsContainer.move(type: .middle)
|
|
}
|
|
}
|
|
|
|
override func viewWillDisappear(_ animated: Bool) {
|
|
super.viewWillDisappear(animated)
|
|
|
|
navBar(hide: false)
|
|
}
|
|
|
|
func navBar(hide: Bool) {
|
|
self.navigationController?.setNavigationBarHidden(hide, animated: true)
|
|
}
|
|
|
|
override func viewDidDisappear(_ animated: Bool) {
|
|
super.viewDidDisappear(animated)
|
|
|
|
mapsContainer.remove()
|
|
mapsContainer = nil
|
|
|
|
NotificationCenter.default.removeObserver(self)
|
|
}
|
|
|
|
// MARK: - StatusBar Style
|
|
|
|
override var preferredStatusBarStyle: UIStatusBarStyle {
|
|
return darkStyle ? .lightContent : .default
|
|
}
|
|
|
|
// MARK: - Rotation Callback
|
|
|
|
@objc func rotated() {
|
|
|
|
let orint = UIDevice.current.orientation
|
|
if orint == .faceUp || orint == .faceDown || orint == .portraitUpsideDown { return }
|
|
|
|
updateMapViewTopPadding()
|
|
}
|
|
|
|
func updateMapViewTopPadding() {
|
|
|
|
let padding: CGFloat = 8
|
|
|
|
statusBarBlurHeight.constant = ContainerDevice.statusBarHeight
|
|
|
|
let paddingX: CGFloat = (ContainerDevice.isIphoneX ? ContainerDevice.isIphoneXTop : padding)
|
|
|
|
var paddingWeatherTop: CGFloat = 0.0
|
|
var paddingTop: CGFloat = 0.0
|
|
var paddingRight: CGFloat = 0.0
|
|
|
|
let paddingStatusBar: CGFloat = (ContainerDevice.statusBarHeight + padding)
|
|
|
|
switch ContainerDevice.orientation {
|
|
case .portrait:
|
|
paddingTop = paddingStatusBar
|
|
paddingRight = padding
|
|
paddingWeatherTop = padding
|
|
case .landscapeLeft:
|
|
paddingTop = ContainerDevice.isIpad ? paddingStatusBar : paddingX
|
|
paddingRight = ContainerDevice.isIphoneX ? 44 : padding
|
|
paddingWeatherTop = paddingX
|
|
case .landscapeRight:
|
|
paddingTop = ContainerDevice.isIpad ? paddingStatusBar : paddingX
|
|
paddingRight = paddingX
|
|
paddingWeatherTop = paddingX
|
|
}
|
|
|
|
var width: CGFloat = 0.0
|
|
var height: CGFloat = 0.0
|
|
|
|
|
|
switch ContainerDevice.orientation {
|
|
case .portrait:
|
|
width = ContainerDevice.screenMin
|
|
height = ContainerDevice.screenMax
|
|
case .landscapeLeft,
|
|
.landscapeRight:
|
|
width = ContainerDevice.screenMax
|
|
height = ContainerDevice.screenMin
|
|
}
|
|
|
|
mapWeatherView.x = (width - mapWeatherView.width - paddingRight)
|
|
|
|
|
|
if ContainerDevice.isPortrait, !ContainerDevice.isIpad {
|
|
mapWeatherView.y = (mapsContainer.view.y - mapWeatherView.height - paddingWeatherTop)
|
|
} else {
|
|
mapWeatherView.y = (height - mapWeatherView.height - paddingWeatherTop)
|
|
}
|
|
|
|
mapButtonsPaddingTop.constant = paddingTop
|
|
mapButtonsPaddingRight.constant = paddingRight
|
|
|
|
mapManager.compass.x = (ContainerDevice.width - 47)
|
|
mapManager.compass.y = (paddingTop + mapButtons.height + 12)
|
|
}
|
|
|
|
// MARK: - Map Manager
|
|
|
|
func updateMapManager() {
|
|
|
|
mapManager = MapViewManager(mapView: mapView)
|
|
mapManager.compass.y = (mapButtons.bottom + 12)
|
|
mapManager.selectPinCallback = { [weak self] in
|
|
guard let _self = self else { return }
|
|
_self.mapsContainer.showLocationDetails()
|
|
}
|
|
mapManager.changeRegionCallback = { [weak self] in
|
|
guard let _self = self else { return }
|
|
_self.mapButtons.changeButtonLocation(fill: false)
|
|
}
|
|
}
|
|
|
|
// MARK: - Map Long-Press
|
|
|
|
@IBAction func handleLong(_ recognizer: UILongPressGestureRecognizer) {
|
|
if routeContainer != nil { return }
|
|
|
|
switch recognizer.state {
|
|
case .began:
|
|
|
|
mapManager.addMapPinFrom(longPress: recognizer)
|
|
mapsContainer.showLocationDetails()
|
|
|
|
default: break
|
|
}
|
|
}
|
|
|
|
|
|
// MARK: - Map Buttons Update
|
|
|
|
func updateMapViewButtons() {
|
|
|
|
mapButtons.addBlur(darkStyle: darkStyle)
|
|
mapButtons.alpha = 0.0
|
|
mapButtons.buttonsActionCallback = { [weak self] (_ index: Int) in
|
|
guard let _self = self else { return }
|
|
|
|
if (index == 0) {
|
|
_self.showMenuContainer()
|
|
} else {
|
|
_self.mapButtons.changeButtonLocation(fill: true)
|
|
|
|
if let coor = _self.mapView.userLocation.location?.coordinate {
|
|
_self.mapView.setCenter(coor, animated: true)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func updateMapViewWeatherView() {
|
|
|
|
mapWeatherView.addBlur(darkStyle: darkStyle)
|
|
mapWeatherView.alpha = 0.0
|
|
}
|
|
|
|
func startAnimationMapElements() {
|
|
|
|
UIView.animate(withDuration: 1.0, animations: { [weak self] in
|
|
guard let _self = self else { return }
|
|
_self.mapWeatherView.alpha = 1.0
|
|
_self.mapButtons.alpha = 1.0
|
|
})
|
|
}
|
|
|
|
// MARK: - Map Buttons Alpha
|
|
|
|
func mapButtons(alpha: CGFloat, animation: Bool = true) {
|
|
if ContainerDevice.isPortrait, !ContainerDevice.isIpad {
|
|
if animation {
|
|
UIView.animate(withDuration: 0.15, animations: { [weak self] in
|
|
guard let _self = self else { return }
|
|
_self.mapButtons.alpha = alpha
|
|
_self.mapWeatherView.alpha = alpha
|
|
})
|
|
} else {
|
|
mapButtons.alpha = alpha
|
|
mapWeatherView.alpha = alpha
|
|
}
|
|
} else {
|
|
mapButtons.alpha = 1.0
|
|
mapWeatherView.alpha = 1.0
|
|
}
|
|
}
|
|
|
|
// MARK: - Change Position MapElements
|
|
|
|
func changePositionMapsElements(container: ContainerController, position: CGFloat, type: ContainerMoveType, animation: Bool) {
|
|
if type == .hide { return }
|
|
|
|
let result = (container.positionMiddle - position)
|
|
var alpha: CGFloat = (1.0 - (result / 20.0))
|
|
|
|
if 20.0 < result {
|
|
alpha = 0.0
|
|
} else if result < 0.0 {
|
|
alpha = 1.0
|
|
}
|
|
|
|
if animation {
|
|
if type == .top {
|
|
mapButtons(alpha: 0.0, animation: true)
|
|
} else {
|
|
mapButtons(alpha: 1.0, animation: true)
|
|
}
|
|
|
|
if type != .top {
|
|
if ContainerDevice.isPortrait, !ContainerDevice.isIpad {
|
|
mapWeatherView.y = (position - mapWeatherView.height - 8)
|
|
}
|
|
}
|
|
|
|
} else {
|
|
mapButtons(alpha: alpha, animation: false)
|
|
|
|
if ContainerDevice.isPortrait, !ContainerDevice.isIpad {
|
|
if result < 0.0 {
|
|
mapWeatherView.y = (position - mapWeatherView.height - 8)
|
|
} else {
|
|
mapWeatherView.y = (container.positionMiddle - mapWeatherView.height - 8)
|
|
}
|
|
}
|
|
}
|
|
|
|
if animation, type != .top {
|
|
view.endEditing(true)
|
|
}
|
|
}
|
|
|
|
// MARK: - Change Index Menu
|
|
|
|
func menuChange(index: Int) {
|
|
|
|
selectedIndex = index
|
|
darkStyle = (index == 2)
|
|
|
|
mapsContainer.update(darkStyle: darkStyle)
|
|
locationContainer?.update(darkStyle: darkStyle)
|
|
routeContainer?.update(darkStyle: darkStyle)
|
|
|
|
mapButtons.addBlur(darkStyle: darkStyle)
|
|
mapWeatherView.addBlur(darkStyle: darkStyle)
|
|
|
|
switch selectedIndex {
|
|
case 0: mapView.mapType = .standard
|
|
case 1: mapView.mapType = .mutedStandard
|
|
case 2: mapView.mapType = .hybrid
|
|
default: break
|
|
}
|
|
|
|
let style: UIBlurEffect.Style = darkStyle ? .systemUltraThinMaterialDark : .regular
|
|
statusBlur.effect = UIBlurEffect(style: style)
|
|
|
|
setNeedsStatusBarAppearanceUpdate()
|
|
}
|
|
|
|
// MARK: - Show Maps-Container
|
|
|
|
func showMapsContainer() {
|
|
mapsContainer = MapsContainerController(addTo: self, darkStyle: darkStyle)
|
|
mapsContainer.mapsDelegate = self
|
|
}
|
|
|
|
// MARK: Delegate
|
|
|
|
func mapsContainerController(showLocationDetails mapsContainerController: MapsContainerController) {
|
|
mapsContainer.move(type: .hide)
|
|
showLocationDetailsContainer()
|
|
mapManager.selectPinAnimation(show: true)
|
|
}
|
|
|
|
func mapsContainerController(move mapsContainerController: MapsContainerController, position: CGFloat, type: ContainerMoveType, animation: Bool) {
|
|
changePositionMapsElements(container: mapsContainerController, position: position, type: type, animation: animation)
|
|
}
|
|
|
|
//MARK: - Show Location-Details
|
|
|
|
func showLocationDetailsContainer() {
|
|
if locationContainer != nil { return }
|
|
|
|
locationContainer = LocationContainerController(addTo: self, darkStyle: darkStyle)
|
|
locationContainer?.locationDelegate = self
|
|
locationContainer?.move(type: ContainerDevice.isPortrait ? .middle : .top)
|
|
}
|
|
|
|
// MARK: Delegate
|
|
|
|
func locationContainerController(showRoute locationContainerController: LocationContainerController) {
|
|
locationContainer?.move(type: .hide)
|
|
showRouteContainer()
|
|
mapManager.showRouteOnMapMyLocation()
|
|
}
|
|
|
|
func locationContainerController(close locationContainerController: LocationContainerController) {
|
|
mapManager.selectPinAnimation(show: false)
|
|
|
|
let animation = locationContainerController.oldMoveType != .middle
|
|
|
|
mapsContainer.move(type: .middle, animation: animation)
|
|
}
|
|
|
|
func locationContainerController(closeComplection locationContainerController: LocationContainerController) {
|
|
locationContainer = nil
|
|
}
|
|
|
|
func locationContainerController(move locationContainerController: LocationContainerController, position: CGFloat, type: ContainerMoveType, animation: Bool) {
|
|
changePositionMapsElements(container: locationContainerController, position: position, type: type, animation: animation)
|
|
}
|
|
|
|
//MARK: - Show Route
|
|
|
|
func showRouteContainer() {
|
|
if routeContainer != nil { return }
|
|
|
|
routeContainer = RouteContainerController(addTo: self, darkStyle: darkStyle)
|
|
routeContainer?.routeDelegate = self
|
|
routeContainer?.move(type: ContainerDevice.isPortrait ? .middle : .top)
|
|
}
|
|
|
|
// MARK: Delegate
|
|
|
|
func routeContainerController(close routeContainerController: RouteContainerController) {
|
|
mapManager.closeRoute(showSelectPin: true)
|
|
locationContainer?.move(type: .middle)
|
|
}
|
|
|
|
func routeContainerController(closeComplection routeContainerController: RouteContainerController) {
|
|
routeContainer = nil
|
|
}
|
|
|
|
func routeContainerController(move routeContainerController: RouteContainerController, position: CGFloat, type: ContainerMoveType, animation: Bool) {
|
|
changePositionMapsElements(container: routeContainerController, position: position, type: type, animation: animation)
|
|
}
|
|
|
|
// MARK: - Show Menu-Container
|
|
|
|
func showMenuContainer() {
|
|
if menuContainer != nil { return }
|
|
|
|
menuContainer = MenuContainerController(addTo: self, darkStyle: darkStyle, selectedIndex: selectedIndex)
|
|
menuContainer?.menuDelegate = self
|
|
|
|
mapButtons(alpha: 0.0)
|
|
}
|
|
|
|
// MARK: Delegate
|
|
|
|
func menuContainerController(close menuContainerController: MenuContainerController) {
|
|
mapButtons(alpha: 1.0)
|
|
}
|
|
|
|
func menuContainerController(closeComplection menuContainerController: MenuContainerController) {
|
|
menuContainer = nil
|
|
}
|
|
|
|
func menuContainerController(segment menuContainerController: MenuContainerController, selectedIndex: Int) {
|
|
menuChange(index: selectedIndex)
|
|
}
|
|
}
|
|
|
|
|
|
|