Swift Mapkit example project - ios

I am new to iOS environment . I have created a map kit app for learning . I added delegate to viewController and also did reference outlet but simulation screen still blank here is my code what is wrong ?
import Foundation
import MapKit
class Places{
var latitute : CLLocationDegrees
var longtitut : CLLocationDegrees
var placeLoc : CLLocationCoordinate2D?
var theAnnotation : MKPointAnnotation?
init (latit : CLLocationDegrees, long :
CLLocationDegrees, mytitle : String ,mysubTitle : String){
self.latitute = latit
self.longtitut = long
setLocation()
setAnnotations(mytitle, subtitle: mysubTitle)
}
func setLocation ( ) {
placeLoc = CLLocationCoordinate2DMake(self.latitute, self.longtitut)
}
func setAnnotations (title : String , subtitle : String ) {
theAnnotation = MKPointAnnotation()
theAnnotation?.coordinate = placeLoc!
theAnnotation?.title = title
theAnnotation?.subtitle = subtitle
}}
ViewController
import UIKit
import MapKit
class ViewController: UIViewController,MKMapViewDelegate {
#IBOutlet weak var myMapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
var latDelta : CLLocationDegrees
var longDelta : CLLocationDegrees
longDelta = 0.01
latDelta = 0.01
var theSpan : MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)
var place1 : Places = Places(latit: 45.995079, long: 54.121006, mytitle: "MyHome", mysubTitle: "Home Sweet Home")
var theRegion : MKCoordinateRegion = MKCoordinateRegionMake(place1.placeLoc!, theSpan)
self.myMapView.setRegion(theRegion, animated: true)
self.myMapView.addAnnotation(place1.theAnnotation)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}}

Take the constraints off to start with and re-run the application, you should see the map appear. Then use the Pin button in the design area to set the constraints. If you want it to fill the view set each vales to 0 and enable the I bar adjacent to each value.
Remember that Xcode 6 is in beta and can be a bit flakey. Also, you might want to confirm its your constraints, go to Debug > View Debugging > Capture View Hierarchy and turn the wireframe on to see where your view is ending up.
Final tip, turn on the assistant editor and change it to preview the storyboard so that you can see how the constraints are impacting on the map view.

Use this below code in vieDidLoad method,no need to create another swift file.It will display annotation with map view.Maybe it will helpful you
var latDelta : CLLocationDegrees
var longDelta : CLLocationDegrees
latDelta = 0.01
longDelta = 0.01
let theSpan:MKCoordinateSpan = MKCoordinateSpanMake(latDelta , longDelta)
let location:CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 13.068452, longitude: 80.25812)
let theRegion:MKCoordinateRegion = MKCoordinateRegionMake(location, theSpan)
mapview.setRegion(theRegion, animated: true)
var anotation = MKPointAnnotation()
anotation.coordinate = location
mapview.addAnnotation(anotation)

Related

Count distance on polyline swift

I've created a map where you can press a start button. The application will then zoom in to your current location, and update the coordinate every 10 second and insert into an array of coordinates. Once I press the stop button, I've a polyline which draws lines between all coordinates. (Like the image below)
So my question is now:
How can I count the distance the polyline was drawn?
//Draw polyline on the map
let aPolyLine = MKPolyline(coordinates: self.locations, count: self.locations.count)
//Adding polyline to mapview
self.mapView.addOverlay(aPolyLine)
let startResult = self.locations.startIndex
let stopResult = self.locations.endIndex
//Retrieve distance and convert into kilometers
let distance = startResult.distance(to: stopResult)
let result = Double(distance) / 1000
let y = Double(round(10 * result)) / 10
self.KiloMeters.text = String(y) + " km"
My guess is that I cannot use startResult.distnace(to: stopResult) because, if I walk in a circle, the kilometer will show 0? right? I'm not sure, but it still dosent work. Nothing is showing when using the code like I've.
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
// MARK: - Variables
let locationManager = CLLocationManager()
// MARK: - IBOutlet
#IBOutlet weak var mapView: MKMapView!
// MARK: - IBAction
#IBAction func distanceTapped(_ sender: UIBarButtonItem) {
let locations: [CLLocationCoordinate2D] = [...]
var total: Double = 0.0
for i in 0..<locations.count - 1 {
let start = locations[i]
let end = locations[i + 1]
let distance = getDistance(from: start, to: end)
total += distance
}
print(total)
}
func getDistance(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D) -> CLLocationDistance {
// By Aviel Gross
// https://stackoverflow.com/questions/11077425/finding-distance-between-cllocationcoordinate2d-points
let from = CLLocation(latitude: from.latitude, longitude: from.longitude)
let to = CLLocation(latitude: to.latitude, longitude: to.longitude)
return from.distance(from: to)
}
}

GMS Mapview flickers, reloads and crashes when updating coordinates

I have been stuck on this problem for the last three weeks and can't seem to get past it, it's driving me insane. I believe that I have all the correct code, but it is just not ordered properly. I am currently designing an app somewhat like Uber, but a completely different concept.
What I am trying to do is pull down coordinates from Firebase and then drop two "pins" or markers on a GMSMapview. I have a UIView classed as GMSMapview and wired up via an IBOutlet. So when the ViewController loads the Google Maps MapView is in the UIView. What I am trying to accomplish is having a "pin" where the current "driver" is and a second pin where "my" location is. What I want to accomplish is for the map to "zoom & follow" the driver until he arrives at my location, similar to Uber.
I have tried hundreds of different types of code combinations and found articles here on StackOverflow that I followed, but those did not work. I was able to get the pins to show up (green is driver, red is where he is going) and when I went into Firebase and changed one of the coordinates the screen would "jump" or flicker really bad. Doing some more research I read that in order to accomplish this concept (and to show all the "cars" as markers on a GMS MapView as Uber does, in another ViewController) I need to put my coordinates in an array, and then loop through a model containing a struct with the variables. Once I did that, the "flickering" stopped, but then the whole view controller continued to reload from scratch(as if the ViewController was just opened for the first time) every time I updated a coordinate. Sometimes I did latitude and other times longitude, but it made no difference. Obviously since I was updating Firebase manually, i could not do both values at the same time.
The articles I found on StackOverflow seemed very promising and I believe I am on the right track, except after implementing some of the recommended code from Google and here, I am now getting a nil crash (identified in my code below). This crash is occurring after I go into Firebase and manually update a coordinate (either lat or long).
After almost a month of trying to tweak and get this code to work, I am looking for some guidance as to where I have gone wrong with my code. I am using the latest PODS for GoogleMaps and Firebase. Bottom line, I am looking for the way to move the GMS Marker while the coordinates update live in Firebase, as well as have the map zoom in as it gets closer to "my location".
Here are the articles I researched and followed:
GMS Map View Maker Flicker issue
How do I move marker along with moving of Google Map in iOS?
Here is my code:
import UIKit
import CoreLocation
import CoreData
import Firebase
import FirebaseDatabase
import FirebaseAuth
import GoogleMaps
import GooglePlaces
import GooglePlacesPicker
import Alamofire
import SwiftyJSON
class SOPOST: UIViewController, CLLocationManagerDelegate, GMSMapViewDelegate, Alertable {
#IBOutlet weak var connectedMapView: GMSMapView!
#IBOutlet weak var driverInfoView: UIView!
let currentUserId = Auth.auth().currentUser?.uid
var markers = [] as NSArray
var locationManager = CLLocationManager()
var placesClient: GMSPlacesClient!
var zoomLevel: Float = 12.0
var likelyPlaces: [GMSPlace] = []
var selectedPlace: GMSPlace?
var custlat: CLLocationDegrees?
var custlong: CLLocationDegrees?
var driverlat: CLLocationDegrees?
var driverlong: CLLocationDegrees?
var destlat: CLLocationDegrees?
var destlong: CLLocationDegrees?
var location: CLLocation?
var destinationMarker = GMSMarker()
var currentCoordAddress: CLLocationCoordinate2D?
var destinationCoordAddress: CLLocationCoordinate2D?
var driverCoordAddress: CLLocationCoordinate2D?
override func viewDidLoad() {
super.viewDidLoad()
connectedMapView.delegate = self
DispatchQueue.main.async {
DataService.instance.REF_TRIPS.observe(.value, with: { (snapshot) in
if let findDriverSnapshot = snapshot.children.allObjects as? [DataSnapshot] {
for driver in findDriverSnapshot {
if driver.childSnapshot(forPath: "passengerKey").value as? String == self.currentUserId! {
let acceptanceStatus = driver.childSnapshot(forPath: "tripIsAccepted").value as! Bool
if acceptanceStatus == true {
if let observeAcceptDict = driver.value as? Dictionary<String, AnyObject> {
let pickupCoordinateArray = observeAcceptDict["pickupCoordinate"] as! NSArray
self.custlat = pickupCoordinateArray[0] as? CLLocationDegrees
self.custlong = pickupCoordinateArray[1] as? CLLocationDegrees
let driverCoordinateArray = observeAcceptDict["driverCoordinate"] as! NSArray
self.markers = observeAcceptDict["driverCoordinate"] as! NSArray
self.driverlat = driverCoordinateArray[0] as? CLLocationDegrees
self.driverlong = driverCoordinateArray[1] as? CLLocationDegrees
let prepareLocation = CLLocation(latitude: self.driverlat!, longitude: self.driverlong!)
self.location = prepareLocation
let destCoordinateArray = observeAcceptDict["destinationCoordinate"] as! NSArray
self.destlat = destCoordinateArray[0] as? CLLocationDegrees
self.destlong = destCoordinateArray[1] as? CLLocationDegrees
self.currentCoordAddress = CLLocationCoordinate2DMake(self.custlat!, self.custlong!)
self.destinationCoordAddress = CLLocationCoordinate2DMake(self.destlat!, self.destlong!)
self.driverCoordAddress = CLLocationCoordinate2DMake(self.driverlat!, self.driverlong!)
CATransaction.begin()
CATransaction.setAnimationDuration(1.0)
self.destinationMarker.position = CLLocationCoordinate2D(latitude: (self.markers[0] as? CLLocationDegrees)!, longitude: (self.markers[1] as? CLLocationDegrees)!)
self.connectedMapView.camera = GMSCameraPosition.camera(withTarget: self.destinationMarker.position, zoom: 12.0)
self.destinationMarker.icon = GMSMarker.markerImage(with: UIColor.green)
self.destinationMarker.map = self.connectedMapView
self.destinationMarker.tracksViewChanges = false
CATransaction.commit()
let customerdestmarker = GMSMarker()
customerdestmarker.position = CLLocationCoordinate2D(latitude: self.custlat!, longitude: self.custlong!)
customerdestmarker.icon = GMSMarker.markerImage(with: UIColor.red)
customerdestmarker.map = self.connectedMapView
customerdestmarker.tracksViewChanges = false
}
}
}
}
}
})
}
}
func updateLocationoordinates(coordinates:CLLocationCoordinate2D) {
if destinationMarker == nil
{
destinationMarker = GMSMarker()
destinationMarker.position = coordinates
let image = UIImage(named:"destinationmarker")
destinationMarker.icon = image
destinationMarker.map = self.connectedMapView
destinationMarker.appearAnimation = GMSMarkerAnimation.pop
}
else
{
CATransaction.begin()
CATransaction.setAnimationDuration(1.0)
destinationMarker.position = coordinates
CATransaction.commit()
}
}
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
self.destinationMarker = GMSMarker(position: self.location!.coordinate) // <----CRASHES HERE: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
destinationMarker.position = position.target
let destinationLocation = CLLocation(latitude: destinationMarker.position.latitude, longitude: destinationMarker.position.longitude)
let destinationCoordinate = destinationLocation.coordinate
updateLocationoordinates(coordinates: destinationCoordinate)
}
}

Swift Writing to and Accessing Class Objects

So I am trying to use MapKit to put a series of points into a map. Ultimitely I am going to get a list of datapoints from csv and load them into the Location Class, then read them from the Class as MapView Annotations. This works here individually for plotting 2 points manually ...but I can't figure out how to properly load and access Location class for a number of items (10 Location Points for example)
Here is my class file
import Foundation
import MapKit
class Location: NSObject, MKAnnotation{
let title: String?
let locationName: String
let discipline: String
let coordinate: CLLocationCoordinate2D
init(title: String, locationName: String, lat: String, lon: String){
self.title = title
self.locationName = locationName
let latDouble = (lat as NSString).doubleValue
let lonDouble = (lon as NSString).doubleValue
let latlong = CLLocationCoordinate2D(latitude: latDouble, longitude: lonDouble)
self.discipline = locationName
self.coordinate = latlong
super.init()
}
}
here is ViewController.swift
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
//var stops = TransitStop[]()
let initialLocation = CLLocation( latitude: 41.880632, longitude: -87.623277)
#IBOutlet weak var mapView: MKMapView!
let regionRadius: CLLocationDistance = 1000
override func viewDidLoad() {
super.viewDidLoad()
let initial = Location(title: "YOU", locationName: "are here", lat: "41.880632", lon: "-87.623277")
let firstStop = Location(title: "ashland", locationName: "ashland", lat: "41.88574", lon: "-87.627835")// Do any additional setup after loading the view, typically from a nib.
centerMapOnLocation(location: initialLocation)
mapView.addAnnotation(initial)
mapView.addAnnotation(firstStop)
}
func centerMapOnLocation(location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, regionRadius * 2.0, regionRadius * 2.0)
mapView.setRegion(coordinateRegion, animated: true)
}
}
Try something like this:
var locationArray: [Location] = []
for line in csvString{
let values:[String] = line.components(separatedBy: ",")
let title = values[0]
let locationName = values[1]
let lat = values[2]
let lng = values[3]
let latD: Double = Double(lat)
let lngD: Double = Double(lng)
let location = Location(title: title, locationName: locationName, lat: latD, lng: lngD)
locationArray.append(location)
}
centerMapOnLocation(location: initialLocation)
addLocationsToMap(locationArray: locationArray)
Make sure you do some validating on the values (Double) before implementing.
Then you can read the location into the map with this function:
func addLocationsToMap(locationArray: [Location]){
for location in locationArray {
mapView.addAnnotation(location)
}
}
Note: I realize the OP doesn't need to explicitly define the variables. I just thought it would be easier to understand.
I assume the csv value can be read from an array perhaps and that the placement of the values in the csv file for 'title', 'loacationName' are well defined.

Thread 1: EXC_BAD_INSTRUCTION

I am making this app where users can see their own location and other users location. I recently just got an error saying
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP,subcode=0x0)
at this line:
var lat = locationManager.location?.coordinate.latitude
I have not managed to fix it.
What is causing it and how can I fix it?
For any who might would like the rest of the code:
import UIKit
import Parse
import CoreLocation
import MapKit
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
var myLocation: [CLLocation] = []
#IBOutlet weak var MapView: MKMapView!
#IBOutlet var UsernameTextField: UITextField!
#IBOutlet var PasswordTF: UITextField!
#IBOutlet var EmailTF: UITextField!
var locationManager: CLLocationManager!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let locationManager = CLLocationManager()
let lat = locationManager.location!.coordinate.latitude
let lon = locationManager.location!.coordinate.longitude
let location = CLLocationCoordinate2D(latitude: lat, longitude: lon)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegionMake(location, span)
MapView!.setRegion(region, animated: true)
let anotation = MKPointAnnotation()
anotation.coordinate = location
anotation.title = "My tittle"
anotation.subtitle = "My Subtitle"
MapView!.addAnnotation(anotation)
print("Welcome in MapViewController")
}
}
This is what #matt is talking about:
The problem is that you are asking for the location manager's location
without checking to see whether the result is nil
Here's how you check to see if your value is nil:
option 1:
guard let lat = locationManager.location?.coordinate.latitude else {
return
}
option 2:
if let latCheck = locationManager.location?.coordinate.latitude {
// assign your lat value here
} else {
// handle the problem
}
You need to change your mindset, when you see an ! you should probably be unwrapping your optional value in one of the above two ways.
update based on comments:
You can also try creating a new variable to work with and see how it works:
guard let location = locationManager.location else {
return
}
then:
let lat = location.coordinate.latitude
let lon = location.coordinate.longitude
The problem is that you are asking for the location manager's location without checking to see whether the result is nil. Well, it is (probably because there has not been time to get the actual location yet). Therefore, when you try to get that location's latitude and longitude, lat and lon, they are nil as well. Therefore when you force-unwrap them, you crash, because you cannot unwrap nil.

In Swift, can I detect if a CLLocation is valid or not?

I am passing a location to another UIViewController which has a MapView using prepareForSegue. I call this receivedLocation. The map centers on the location that is passed. If the user clicks a button to take them to the map without first sending a location, won't this fail?
How can I test to see if receivedLocation actually contains data, so that I can set the map to center on something else if it's empty?
Would creating a Boolean variable, and initially setting it false but turning it to true when passing a location and testing that work as I desire?
I see something called CLLocationCoordinateIsValid is available, but its parameter is a 2DCoordinate, where I run into the same problem of testing whether or not receivedLocation.
I'm fairly new at this and tried looking for an answer already but couldn't find an appropriate one. Thank you!
import UIKit
import MapKit
import CoreLocation
class mapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet var map: MKMapView!
#IBOutlet var notes: UITextView!
var receivedLocation = CLLocation()
var annotation = MKPointAnnotation()
var currentManager:CLLocationManager!
var latitute:CLLocationDegrees = CLLocationDegrees()
var longitude:CLLocationDegrees = CLLocationDegrees()
override func viewDidLoad() {
super.viewDidLoad()
map.layer.cornerRadius = 12.0
notes.layer.cornerRadius = 12.0
currentManager = CLLocationManager()
currentManager.delegate = self
currentManager.desiredAccuracy = kCLLocationAccuracyBest
currentManager.startUpdatingLocation()
if CLLocationCoordinate2DIsValid(receivedLocation.coordinate) {
latitute = receivedLocation.coordinate.latitude
longitude = receivedLocation.coordinate.longitude
annotation.coordinate.latitude = receivedLocation.coordinate.latitude
annotation.coordinate.longitude = receivedLocation.coordinate.longitude
map.addAnnotation(annotation)
} else {
latitute = currentManager.location.coordinate.latitude
longitude = currentManager.location.coordinate.longitude
}
var latDelta: CLLocationDegrees = 0.01
var lonDelta: CLLocationDegrees = 0.01
var span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, lonDelta)
var location : CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitute, longitude)
var region: MKCoordinateRegion = MKCoordinateRegionMake(location, span)
map.setRegion(region, animated: false)
}
So far this is not working for me. CLLocationCoordinate2DIsValid always returns true and centers around an island, as well as adds an annotation to that weird spot.
First of all, do not needlessly put a useless CLLocation() into receivedLocation. Declare receivedLocation as an implicitly unwrapped Optional:
var receivedLocation : CLLocation! = nil
That way, either someone has set it or they have not. If they have not, it is nil, and you can test that:
if receivedLocation != nil {
// okay, it's a real location!
}
Second, your else is never going to work, because it takes time for the sensors to warm up and get a location - in fact, they may never get one. So I can pretty well guarantee that in the case where receivedLocation is nil, currentManager.location will not be any use either. (See my answer here for an explanation, and pointer to sample code, of how to use a location manager to get a single location value.)

Resources