// // 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 var textSteps: [MKRoute.Step]? // 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 howToUseGuibeBtn: UIButton! // --------------------- @IBOutlet weak var searchBarBg: UIButton! @IBOutlet weak var searchView: UIView! @IBOutlet weak var searchBar: UISearchBar! @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 xNaviBtn: UIButton! // --------------------- @IBOutlet weak var cancelBtn: UIButton! // --------------------- @IBOutlet weak var persEtaView: UIView! @IBOutlet weak var persEtaLbl: UILabel! // --------------------- @IBOutlet weak var headingButton: UIButton! // --------------------- @IBOutlet weak var naviFinishedView: UIView! @IBOutlet weak var arrivedAtDestinationBtn: UIButton! @IBOutlet weak var debugLbl: 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.appDelegate.naviStarted = true 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}) self.playStart() } } @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 self.appDelegate.naviStarted = 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 }) self.debugLbl.text = "–––" } @IBAction func keybDismissBtnPressed(_ sender: Any) { searchBar.endEditing(true) self.keybDismissBtn.isHidden = true } // -------------------------- @IBAction func arrivedAtDestinationBtnPressed(_ sender: Any) { UIView.animate(withDuration: 1, animations: {self.naviFinishedView.alpha = 0.0}) } @IBAction func xDebug(_ sender: Any) { finishedNavigationSuccessfully() } // ACTIONS--------------ACTIONS override func viewDidLoad() { super.viewDidLoad() overrideUserInterfaceStyle = .light let debugVar = defaults.integer(forKey: "debug") if debugVar == 2 { debugLbl.alpha = 1.0 } else { debugLbl.alpha = 0.0 } 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 headingButton.layer.shadowColor = UIColor.black.cgColor headingButton.layer.cornerRadius = 7 headingButton.layer.shadowOffset = CGSize(width: 5, height: 7) headingButton.layer.shadowRadius = 7 headingButton.layer.shadowOpacity = 0.2 naviFinishedView.alpha = 0.0 arrivedAtDestinationBtn.layer.shadowColor = UIColor.black.cgColor arrivedAtDestinationBtn.layer.cornerRadius = 50 arrivedAtDestinationBtn.layer.shadowOffset = CGSize(width: 5, height: 7) arrivedAtDestinationBtn.layer.shadowRadius = 50 arrivedAtDestinationBtn.layer.shadowOpacity = 0.6 menuBtn.accessibilityLabel = "Menu" headingButton.accessibilityLabel = "Heading" searchBar.accessibilityLabel = "Search destination" cancelBtn.accessibilityLabel = "Cancel navigation" xNaviBtn.accessibilityLabel = "Cancel" startNaviBtn.accessibilityLabel = "Start Navigation" dismissBtn.accessibilityLabel = "Dismiss menu" keybDismissBtn.accessibilityLabel = "Dismiss keyboard" mapView.accessibilityElementsHidden = true startNaviImg.accessibilityLabel = "Follow the vibe" arrivedAtDestinationBtn.accessibilityLabel = "End Navigation" howToUseGuibeBtn.accessibilityLabel = "How to use ghibe" } //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) 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 startDate = Date() let ETATime = startDate.addingTimeInterval(TimeInterval(travelTime*60)) let ETAVarLbl = DateFormatter.localizedString(from: ETATime, dateStyle: DateFormatter.Style.none, timeStyle: DateFormatter.Style.short) self.textSteps = primaryRoute.steps self.appDelegate.textSteps = self.textSteps self.appDelegate.currentStep = self.stepCounter // let ivc = self.storyboard?.instantiateViewController(withIdentifier: "ivc") as! InstructionsViewController // let wivc = WrittenInstructionsViewController() // wivc.stepBySteps = self.textSteps // wivc.currentStep = self.stepCounter // ivc.stepsText = self.textSteps // ivc.currentStep = self.stepCounter 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)) if player.volume != 1.0 {player.setVolume(1.0, fadeDuration: 1.0)} 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() finishedNavigationSuccessfully() default: playErr() break; } // let speechUtterance = AVSpeechUtterance(string: message) // speechSynthesizer.speak(speechUtterance) previousDistanceToWaypoint = 1000 } else { // let speechUtterance = AVSpeechUtterance(string: message) // speechSynthesizer.speak(speechUtterance) playDone() finishedNavigationSuccessfully() stepCounter = 0 previousDistanceToWaypoint = 1000 searchBar.isHidden = false // locationManager.monitoredRegions.forEach({ self.locationManager.stopMonitoring(for: $0) }) } } } } func finishedNavigationSuccessfully(){ self.stepCounter = 0 self.startedNavigation = false self.appDelegate.naviStarted = false mapView.removeOverlays(mapView.overlays) DispatchQueue.main.asyncAfter(deadline: .now() + 1){ UIView.animate(withDuration: 0.7, animations: { self.searchView.alpha = 1.0 self.cancelBtn.alpha = 0.0 self.persEtaView.alpha = 0.0}) } UIView.animate(withDuration: 1, animations: {self.naviFinishedView.alpha = 1.0}) //Animate alpha doneview and deanimate it } func playStart(){ let path = Bundle.main.path(forResource: "guibe_begin", ofType : "mp3")! self.debugLbl.text = String(path.suffix(15)) let url = URL(fileURLWithPath : path) do { player = try AVAudioPlayer(contentsOf: url) player.delegate = self player.play() } catch {} } func playRight(){ // currentStepLbl.text = "COM: RIGHT" let var1 = "guibe_left" let var2 = "guibe_right" var path: String = "" if defaults.integer(forKey: "handSide") == 1{ if defaults.integer(forKey: "bigSide") == 1{ path = Bundle.main.path(forResource: var1, ofType : "mp3")! } else if defaults.integer(forKey: "bigSide") == 2 { path = Bundle.main.path(forResource: var2, ofType : "mp3")! } else { errorMsg() } } else if defaults.integer(forKey: "handSide") == 2 { if defaults.integer(forKey: "bigSide") == 1{ path = Bundle.main.path(forResource: var2, ofType : "mp3")! } else if defaults.integer(forKey: "bigSide") == 2 { path = Bundle.main.path(forResource: var1, ofType : "mp3")! } else { errorMsg() } } else { errorMsg() } self.debugLbl.text = String(path.suffix(15)) let url = URL(fileURLWithPath : path) do { player = try AVAudioPlayer(contentsOf: url) player.delegate = self player.play() } catch { errorMsg() } } func playLeft(){ // currentStepLbl.text = "COM: LEFT" let var1 = "guibe_right" let var2 = "guibe_left" var path: String = "" if defaults.integer(forKey: "handSide") == 1{ if defaults.integer(forKey: "bigSide") == 1{ path = Bundle.main.path(forResource: var1, ofType : "mp3")! } else if defaults.integer(forKey: "bigSide") == 2 { path = Bundle.main.path(forResource: var2, ofType : "mp3")! } else { errorMsg() } } else if defaults.integer(forKey: "handSide") == 2 { if defaults.integer(forKey: "bigSide") == 1{ path = Bundle.main.path(forResource: var2, ofType : "mp3")! } else if defaults.integer(forKey: "bigSide") == 2 { path = Bundle.main.path(forResource: var1, ofType : "mp3")! } else { errorMsg() } } else { errorMsg() } self.debugLbl.text = String(path.suffix(15)) let url = URL(fileURLWithPath : path) do { player = try AVAudioPlayer(contentsOf: url) player.delegate = self player.play() } catch { errorMsg() } } func playErr(){ // currentStepLbl.text = "COM: ERROR!!!" return } func playTurnArd(){ // currentStepLbl.text = "COM: UTURN" let path = Bundle.main.path(forResource: "guibe_wrongdirection", ofType : "mp3")! self.debugLbl.text = String(path.suffix(15)) let url = URL(fileURLWithPath : path) do { player = try AVAudioPlayer(contentsOf: url) player.delegate = self player.play() } catch {} } func playDone(){ // currentStepLbl.text = "COM: DONE" let path = Bundle.main.path(forResource: "guibe_arrived", ofType : "mp3")! self.debugLbl.text = String(path.suffix(15)) let url = URL(fileURLWithPath : path) do { player = try AVAudioPlayer(contentsOf: url) player.delegate = self player.play() } catch {} } func errorMsg() { let alert = UIAlertController(title: "Playback Error", message: "Error in either keyValues or filename", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in switch action.style{ case .default: self.dismiss(animated: true, completion: nil) case .cancel: return case .destructive: return }})) self.present(alert, animated: true, completion: nil) } } 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() } }