Custom MKAnnotation disappears when I alter it's coordinate - ios

I don't exactly know if this has been answered before, but I have not been able to find it so I thought I would post a question.
I have a custom MKAnnotation that shows up perfectly fine if i use coordinates such as : "74.8044595495237 -21.7716836352598"
var currLat: Double! = 74.8044595495237
var currLong: Double! = -21.7716836352598
let egg: EggPin = EggPin(coordinate: CLLocationCoordinate2DMake(currLat, currLong), title: "Egg", subtitle: "This is an egg")
That works just fine. The custom MKAnnotation shows up on the map and everything is fine and dandy. But, when I change the coordinates of the EggPin by subtracting 1.0 from currLat and currLong like so:
var currLat: Double! = 74.8044595495237
var currLong: Double! = -21.7716836352598
let egg: EggPin = EggPin(coordinate: CLLocationCoordinate2DMake(currLat - 1.0, currLong - 1.0), title: "Egg", subtitle: "This is an egg")
The MKAnnotation disappears. I am new to MapKit so I feel like I must be missing something very simple about setting coordinates. Below is my EggPin Class if that will help :
import Foundation
import MapKit
class EggPin: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
super.init()
}
}

The problem was that the MKAnnoation went way off the screen when I subtracted 1.0 from it, but subtracting .005 worked fine as it was shown by the pin.

Related

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)
}
}

Convert MKAnnotation array to set

I have a situation where I have map annotations as [MKAnnotation] array in swift. Now I need to convert this into a set for some operation. How can I do this in swift? Basically I need to add only the non existent annotations on the map while updating the map view.
You can find out if an annotation exists on the map by using mapView.view(for:) as mentioned here:
if (self.mapView.view(for: annotation) != nil) {
print("pin already on mapview")
}
"Basically I need to add only the non existent annotations on the map while updating the map view."
We first need to define what makes two annotations equal (in your scenario). Once that is clear you an override the isEqual method. You can then add annotations to a Set.
Here is an example :
class MyAnnotation : NSObject,MKAnnotation{
var coordinate: CLLocationCoordinate2D
var title: String?
convenience init(coord : CLLocationCoordinate2D, title: String) {
self.init()
self.coordinate = coord
self.title = title
}
private override init() {
self.coordinate = CLLocationCoordinate2D(latitude: 0, longitude: 0)
}
override func isEqual(_ object: Any?) -> Bool {
if let annot = object as? MyAnnotation{
// Add your defintion of equality here. i.e what determines if two Annotations are equal.
return annot.coordinate.latitude == coordinate.latitude && annot.coordinate.longitude == coordinate.longitude && annot.title == title
}
return false
}
}
In the code above two instances of MyAnnotation are considered equal when you they have the same coordinates and the same title.
let ann1 = MyAnnotation(coord: CLLocationCoordinate2D(latitude: 20.0, longitude: 30.0), title: "Annot A")
let ann2 = MyAnnotation(coord: CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0), title: "Annot B")
let ann3 = MyAnnotation(coord: CLLocationCoordinate2D(latitude: 20.0, longitude: 30.0), title: "Annot A")
var annSet = Set<MyAnnotation>()
annSet.insert(ann1)
annSet.insert(ann2)
annSet.insert(ann3)
print(annSet.count) // Output : 2 (ann1 & ann3 are equal)

Tap MKAnnotationView with XCTestCase

I want to tap MKAnnotationView using my XCTestCase with UITest, so I have my mapView as a XCUIElement. I have no idea how to get annotations from mapView, I don't want to put fixed positions on this on the screens because it is tested on various devices. So I'm stuck on the
let mapView:XCUIElement = app.maps.elementBoundByIndex(0)
Any ideas?
To interact with a map annotation use otherElements referenced by the title attribute.
For example, in your production code set a title in your MKAnnotation.
class Annotation: NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
let title: String?
init(title: String?, coordinate: CLLocationCoordinate2D) {
self.title = title
self.coordinate = coordinate
}
}
let coordinate = CLLocationCoordinate2DMake(40.703490, -73.987770)
let annotation = Annotation(title: "BeerMenus HQ", coordinate: coordinate)
let mapView = MKMapView()
mapView.addAnnotation(annotation)
Then under test you can reference the annotation via it's title attribute.
let app = XCUIApplication()
let annotation = app.maps.element.otherElements["BeerMenus HQ"]
annotation.tap()

MKAnnotation subclass issues

I upgraded to Swift 1.2 last night, and I got a bug I really can't figure out. The below code worked fine in the previous version of Xcode and Swift.
//MARK: Annotation Object
class PointAnnotation : NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String
var subtitle: String
var point: Point
var image: UIImage
var md: String
init(point: Point) {
self.coordinate = point.coordinate
self.title = point.title
self.subtitle = point.teaser
self.image = UIImage(named: "annotation.png")!
self.point = point
self.md = point.content
}
}
On line 3, I get the somewhat hard to understand error
Objective-C method 'setCoordinate:' provided by the setter for 'coordinate' conflicts with the optional requirement method 'setCoordinate' in protocol 'MKAnnotation' I tried changing variable names and such, but no help. Does anyone have any idea how to fix this?
The class is for annotations on my mapview.
If you do not require to change coordinates after initialization then you can use it that way. It works for me with Swift 1.2:
class CustomAnnotation : NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
var title: String
init(coordinate: CLLocationCoordinate2D, title: String) {
self.coordinate = coordinate
self.title = title
}
}
You are overriding a readonly ivar in MKAnnotation with a readwrite.
var coordinate: CLLocationCoordinate2D { get }
I had the same issue so I just did this:
private var _coordinate: CLLocationCoordinate2D
var coordinate: CLLocationCoordinate2D {
return _coordinate
}
init(coordinate: CLLocationCoordinate2D) {
self._coordinate = coordinate
super.init()
}
func setCoordinate(newCoordinate: CLLocationCoordinate2D) {
self._coordinate = newCoordinate
}
That way coordinate is still readonly and you can use the setCoordinate method.

Swift Mapkit example project

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)

Resources