guide/Guibe/MainViewController.swift
2019-06-15 16:51:18 +01:00

563 lines
21 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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: UIView!
// ---------------------
@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()
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 = "this is How to use geibe"
} //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)
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_long_left"
let var2 = "guibe_long_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_long_right"
let var2 = "guibe_long_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()
}
}