404 lines
15 KiB
Swift
404 lines
15 KiB
Swift
//
|
|
// MainViewController.swift
|
|
// Guibe
|
|
//
|
|
// Created by Max Hunt on 28/05/2019.
|
|
// Copyright © 2019 8. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import AVKit
|
|
import MapKit
|
|
import CoreLocation
|
|
import AVFoundation
|
|
|
|
|
|
class MainViewController: UIViewController, AVAudioPlayerDelegate, myProtocol {
|
|
|
|
|
|
// DELETE
|
|
let defaults = UserDefaults.standard
|
|
// DELETE
|
|
|
|
let appDelegate = UIApplication.shared.delegate as! AppDelegate
|
|
|
|
var etaTime: Int = -1
|
|
|
|
let locationManager = CLLocationManager()
|
|
var currentCoordinate: CLLocationCoordinate2D!
|
|
|
|
var player = AVAudioPlayer()
|
|
|
|
var steps = [MKRoute.Step]()
|
|
|
|
var stepCounter = 0
|
|
|
|
var previousDistanceToWaypoint: Double = 1000
|
|
var distanceToNextCoord: Double = 1000
|
|
|
|
var startedNavigation: Bool = false
|
|
|
|
|
|
// OUTLETS--------------OUTLETS
|
|
@IBOutlet weak var menuWindowView: UIView!
|
|
@IBOutlet weak var menuView: UIView!
|
|
@IBOutlet weak var menuBtn: UIButton!
|
|
@IBOutlet weak var dismissBtn: UIButton!
|
|
// ---------------------
|
|
@IBOutlet weak var searchBarBg: UIButton!
|
|
@IBOutlet weak var searchView: UIView!
|
|
@IBOutlet weak var searchBar: UISearchBar!
|
|
@IBOutlet weak var micBtn: UIButton!
|
|
@IBOutlet weak var keybDismissBtn: UIButton!
|
|
// ---------------------
|
|
@IBOutlet weak var mapView: MKMapView!
|
|
// ---------------------
|
|
@IBOutlet weak var etaView: UIView!
|
|
@IBOutlet weak var startNaviBtn: UIButton!
|
|
@IBOutlet weak var etaLabel: UILabel!
|
|
@IBOutlet weak var startNaviImg: UIImageView!
|
|
// ---------------------
|
|
@IBOutlet weak var cancelBtn: UIButton!
|
|
// ---------------------
|
|
@IBOutlet weak var persEtaView: UIView!
|
|
@IBOutlet weak var persEtaLbl: UILabel!
|
|
// OUTLETS--------------OUTLETS
|
|
|
|
// ACTIONS--------------ACTIONS
|
|
@IBAction func menuBtnPressed(_ sender: Any) {
|
|
UIView.animate(withDuration: 0.2, animations: {self.menuWindowView.alpha = 1.0})
|
|
dismissBtn.isHidden = false
|
|
}
|
|
@IBAction func micBtnPressed(_ sender: Any) {
|
|
}
|
|
@IBAction func dismissBtnPressed(_ sender: Any) {
|
|
UIView.animate(withDuration: 0.2, animations: {self.menuWindowView.alpha = 0.0})
|
|
dismissBtn.isHidden = true
|
|
}
|
|
@IBAction func settingsBtnPressed(_ sender: Any) {
|
|
}
|
|
@IBAction func followBtnPressed(_ sender: Any) {
|
|
self.mapView.userTrackingMode = .follow
|
|
}
|
|
@IBAction func headingBtnPressed(_ sender: Any) {
|
|
self.mapView.userTrackingMode = .followWithHeading
|
|
}
|
|
// --------------------------
|
|
@IBAction func startNaviPressed(_ sender: Any) {
|
|
self.startedNavigation = true
|
|
self.stepCounter += 1
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
|
self.searchBar.text = ""
|
|
self.etaView.alpha = 0.0
|
|
self.searchView.alpha = 0.0
|
|
self.cancelBtn.alpha = 1.0
|
|
self.persEtaView.alpha = 1.0
|
|
}
|
|
|
|
UIView.animate(withDuration: 0.5, animations: {self.startNaviImg.alpha = 1.0})
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
|
UIView.animate(withDuration: 0.5, animations: {self.startNaviImg.alpha = 0.0})
|
|
}
|
|
}
|
|
@IBAction func xNaviPressed(_ sender: Any) {
|
|
UIView.animate(withDuration: 0.5, animations: {self.etaView.alpha = 0.0})
|
|
mapView.removeOverlays(mapView.overlays)
|
|
}
|
|
@IBAction func cancelBtnPressed(_ sender: Any) {
|
|
self.stepCounter = 0
|
|
self.startedNavigation = false
|
|
mapView.removeOverlays(mapView.overlays)
|
|
UIView.animate(withDuration: 0.3, animations: {
|
|
self.searchView.alpha = 1.0
|
|
self.cancelBtn.alpha = 0.0
|
|
self.persEtaView.alpha = 0.0
|
|
})
|
|
}
|
|
|
|
@IBAction func keybDismissBtnPressed(_ sender: Any) {
|
|
searchBar.endEditing(true)
|
|
self.keybDismissBtn.isHidden = true
|
|
}
|
|
|
|
// --------------------------
|
|
|
|
// ACTIONS--------------ACTIONS
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
searchBar.delegate = self
|
|
searchBarBg.layer.shadowColor = UIColor.black.cgColor
|
|
searchBarBg.layer.cornerRadius = 10
|
|
searchBarBg.layer.shadowOffset = CGSize(width: 5, height: 7)
|
|
searchBarBg.layer.shadowRadius = 10
|
|
searchBarBg.layer.shadowOpacity = 0.2
|
|
|
|
keybDismissBtn.isHidden = true
|
|
|
|
menuWindowView.alpha = 0.0
|
|
menuWindowView.layer.shadowColor = UIColor.black.cgColor
|
|
menuWindowView.layer.cornerRadius = 13
|
|
menuWindowView.layer.shadowOffset = CGSize(width: 5, height: 7)
|
|
menuWindowView.layer.shadowRadius = 10
|
|
menuWindowView.layer.shadowOpacity = 0.2
|
|
|
|
dismissBtn.isHidden = true
|
|
|
|
mapView.delegate = self
|
|
|
|
etaView.alpha = 0.0
|
|
etaView.layer.shadowColor = UIColor.black.cgColor
|
|
etaView.layer.shadowOffset = CGSize(width: 0, height: -10)
|
|
etaView.layer.shadowRadius = 10
|
|
etaView.layer.shadowOpacity = 0.2
|
|
|
|
cancelBtn.alpha = 0.0
|
|
// cancelBtn.isHidden = true
|
|
cancelBtn.layer.shadowColor = UIColor.black.cgColor
|
|
cancelBtn.layer.cornerRadius = 15
|
|
cancelBtn.layer.shadowOffset = CGSize(width: 3, height: 6)
|
|
cancelBtn.layer.shadowRadius = 10
|
|
cancelBtn.layer.shadowOpacity = 0.2
|
|
|
|
startNaviBtn.layer.shadowColor = UIColor.black.cgColor
|
|
startNaviBtn.layer.cornerRadius = 13
|
|
startNaviBtn.layer.shadowOffset = CGSize(width: 5, height: 7)
|
|
startNaviBtn.layer.shadowRadius = 10
|
|
startNaviBtn.layer.shadowOpacity = 0.2
|
|
|
|
locationManager.delegate = self
|
|
locationManager.desiredAccuracy = kCLLocationAccuracyBest
|
|
locationManager.startUpdatingLocation()
|
|
mapView.userTrackingMode = .follow
|
|
|
|
startNaviImg.alpha = 0.0
|
|
|
|
persEtaView.alpha = 0.0
|
|
persEtaView.layer.shadowColor = UIColor.black.cgColor
|
|
persEtaView.layer.cornerRadius = 7
|
|
persEtaView.layer.shadowOffset = CGSize(width: 5, height: 7)
|
|
persEtaView.layer.shadowRadius = 7
|
|
persEtaView.layer.shadowOpacity = 0.2
|
|
|
|
|
|
} //END OF VIEW DID LOAD
|
|
|
|
func getDirections(to destination: MKMapItem) {
|
|
let sourcePlacemark = MKPlacemark(coordinate: currentCoordinate)
|
|
let sourceMapItem = MKMapItem(placemark: sourcePlacemark)
|
|
|
|
let directionsRequest = MKDirections.Request()
|
|
directionsRequest.source = sourceMapItem
|
|
directionsRequest.destination = destination
|
|
directionsRequest.transportType = .walking
|
|
|
|
let directions = MKDirections(request: directionsRequest)
|
|
directions.calculate { (response, _) in
|
|
guard let response = response else { return }
|
|
|
|
guard let primaryRoute = response.routes.first else { return }
|
|
|
|
self.mapView.addOverlay(primaryRoute.polyline)
|
|
|
|
// directions.calculateETA { (etaResponse, error) -> Void in
|
|
// if let error = error {
|
|
// print("Error while requesting ETA : \(error.localizedDescription)")
|
|
// //travelTime = "Not Available"
|
|
// }else{
|
|
// self.etaMinutes = Int((etaResponse?.expectedTravelTime)!/60)
|
|
// }
|
|
// }
|
|
|
|
self.steps = primaryRoute.steps
|
|
for i in 0 ..< primaryRoute.steps.count {
|
|
let step = primaryRoute.steps[i]
|
|
print(step.instructions)
|
|
print(step.distance)
|
|
// -----------------------------Geofencing setup
|
|
let region = CLCircularRegion(center: step.polyline.coordinate,
|
|
radius: 15,
|
|
identifier: "\(i)")
|
|
// self.locationManager.startMonitoring(for: region)
|
|
// -----------------------------Geofencing setup
|
|
let circle = MKCircle(center: region.center, radius: region.radius)
|
|
self.mapView.addOverlay(circle)
|
|
}
|
|
|
|
let travelTime = Int(primaryRoute.expectedTravelTime/60)
|
|
// let x = DateFormatter.localizedString(from: NSDate() as Date, dateStyle: DateFormatter.Style.none, timeStyle: DateFormatter.Style.short)
|
|
let startDate = Date()
|
|
let ETATime = startDate.addingTimeInterval(TimeInterval(travelTime*60))
|
|
let ETAVarLbl = DateFormatter.localizedString(from: ETATime, dateStyle: DateFormatter.Style.none, timeStyle: DateFormatter.Style.short)
|
|
|
|
|
|
self.etaLabel.text = "\(ETAVarLbl)"
|
|
self.persEtaLbl.text = "\(ETAVarLbl)"
|
|
UIView.animate(withDuration: 0.3, animations: {self.etaView.alpha = 1.0})
|
|
|
|
// self.startedNavigation = true
|
|
// self.stepCounter += 1
|
|
}
|
|
}
|
|
|
|
func startNavigation(toPlace: MKMapItem){
|
|
getDirections(to: toPlace)
|
|
}
|
|
|
|
} //END OF CLASS
|
|
|
|
|
|
extension MainViewController: CLLocationManagerDelegate {
|
|
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
|
guard let currentLocation = locations.first else { return }
|
|
currentCoordinate = currentLocation.coordinate
|
|
|
|
|
|
if startedNavigation == true {
|
|
let nextStep = steps[stepCounter]
|
|
let nextCoord = CLLocation(latitude: nextStep.polyline.coordinate.latitude, longitude: nextStep.polyline.coordinate.longitude)
|
|
distanceToNextCoord = currentLocation.distance(from: nextCoord)
|
|
|
|
if distanceToNextCoord < previousDistanceToWaypoint {
|
|
previousDistanceToWaypoint = currentLocation.distance(from: nextCoord)
|
|
}
|
|
if distanceToNextCoord > previousDistanceToWaypoint + 10 {
|
|
previousDistanceToWaypoint = distanceToNextCoord - 5
|
|
playTurnArd()
|
|
}
|
|
if distanceToNextCoord < 15 {
|
|
stepCounter += 1
|
|
if stepCounter < steps.count {
|
|
// let currentStep = steps[stepCounter]
|
|
let message = "\(steps[stepCounter-1].instructions)"
|
|
let maneuverCommand = String(message.prefix(10))
|
|
switch (maneuverCommand) {
|
|
case "Turn right":
|
|
playRight()
|
|
break;
|
|
case "Bear right":
|
|
playRight()
|
|
break;
|
|
case "Turn left ":
|
|
playLeft()
|
|
break;
|
|
case "Bear left":
|
|
playLeft()
|
|
break;
|
|
case "The destin":
|
|
playDone()
|
|
|
|
default:
|
|
playErr()
|
|
break;
|
|
}
|
|
|
|
// let speechUtterance = AVSpeechUtterance(string: message)
|
|
// speechSynthesizer.speak(speechUtterance)
|
|
previousDistanceToWaypoint = 1000
|
|
} else {
|
|
// let speechUtterance = AVSpeechUtterance(string: message)
|
|
// speechSynthesizer.speak(speechUtterance)
|
|
stepCounter = 0
|
|
previousDistanceToWaypoint = 1000
|
|
searchBar.isHidden = false
|
|
// locationManager.monitoredRegions.forEach({ self.locationManager.stopMonitoring(for: $0) })
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func playRight(){
|
|
// currentStepLbl.text = "COM: RIGHT"
|
|
let path = Bundle.main.path(forResource: "xright", ofType : "mp3")!
|
|
let url = URL(fileURLWithPath : path)
|
|
do {
|
|
player = try AVAudioPlayer(contentsOf: url)
|
|
player.delegate = self
|
|
player.play()
|
|
} catch {}
|
|
}
|
|
|
|
func playLeft(){
|
|
// currentStepLbl.text = "COM: LEFT"
|
|
let path = Bundle.main.path(forResource: "xleft", ofType : "mp3")!
|
|
let url = URL(fileURLWithPath : path)
|
|
do {
|
|
player = try AVAudioPlayer(contentsOf: url)
|
|
player.delegate = self
|
|
player.play()
|
|
} catch {}
|
|
}
|
|
|
|
func playErr(){
|
|
// currentStepLbl.text = "COM: ERROR!!!"
|
|
return
|
|
}
|
|
|
|
func playTurnArd(){
|
|
// currentStepLbl.text = "COM: UTURN"
|
|
let path = Bundle.main.path(forResource: "xboth", ofType : "mp3")!
|
|
let url = URL(fileURLWithPath : path)
|
|
do {
|
|
player = try AVAudioPlayer(contentsOf: url)
|
|
player.delegate = self
|
|
player.play()
|
|
} catch {}
|
|
}
|
|
|
|
func playDone(){
|
|
// currentStepLbl.text = "COM: DONE"
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
extension MainViewController: UISearchBarDelegate {
|
|
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
|
searchBar.endEditing(true) //HIDES LE KEYBOARD
|
|
self.keybDismissBtn.isHidden = true
|
|
|
|
let localSearchRequest = MKLocalSearch.Request()
|
|
localSearchRequest.naturalLanguageQuery = searchBar.text
|
|
let region = MKCoordinateRegion(center: currentCoordinate, span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
|
|
localSearchRequest.region = region
|
|
let localSearch = MKLocalSearch(request: localSearchRequest)
|
|
localSearch.start { (response, _) in
|
|
guard let response = response else { return }
|
|
|
|
|
|
|
|
let resultsViewController = self.storyboard?.instantiateViewController(withIdentifier: "resultsScreen") as! TableViewController
|
|
resultsViewController.searchResults = response.mapItems
|
|
resultsViewController.modalTransitionStyle = .coverVertical
|
|
resultsViewController.myProtocol = self
|
|
self.present(resultsViewController, animated: true, completion: nil)
|
|
}
|
|
}
|
|
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
|
|
self.keybDismissBtn.isHidden = false
|
|
}
|
|
}
|
|
extension MainViewController: MKMapViewDelegate {
|
|
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
|
|
if overlay is MKPolyline {
|
|
let renderer = MKPolylineRenderer(overlay: overlay)
|
|
renderer.strokeColor = .blue
|
|
renderer.alpha = 0.7
|
|
renderer.lineWidth = 10
|
|
return renderer
|
|
}
|
|
if overlay is MKCircle {
|
|
let renderer = MKCircleRenderer(overlay: overlay)
|
|
renderer.strokeColor = .red
|
|
renderer.fillColor = .red
|
|
renderer.alpha = 0.4
|
|
return renderer
|
|
}
|
|
return MKOverlayRenderer()
|
|
}
|
|
}
|