Using multiple annotations within Xcode - ios

I would like to provide multiple MapKit annotations (pins) in Xcode 7.0.1 but have no idea how to, I've created one annotation:
let location = CLLocationCoordinate2DMake(50.714141, -1.874704)
let annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = "Bournemouth Pier"
mapView.addAnnotation(annotation)
It seems I can't just copy and paste this code with different titles and coordinates which is really annoying and quite frankly stupid. (you can tell how annoyed I am) any help would be appreciated cheers.

You definitely can add multiple annotations this way. Make sure you instantiate a new MKPointAnnotation for each one, though.
For example:
var location = CLLocationCoordinate2DMake(50.714141, -1.874704)
var annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = "Bournemouth Pier"
mapView.addAnnotation(annotation)
location = CLLocationCoordinate2DMake(51.714141, -2.874704)
annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = "Gwernesney"
mapView.addAnnotation(annotation)
If you're still having trouble adding multiple annotations, please edit your question, showing us how you are trying to do that. You've shown an attempt at adding a single annotation, but not how you've attempted to add multiple ones.

Related

Swift MKMapView.setRegion not working after MKLocalSearch.Request

I've been working on an app to annotate an MKMapView with all nightlife locations near a user. I have implemented a recenter function that is called by a SwiftUI button. The recenter works fine based on CLLocationManager.location, but after any MKLocalSearch query, the setRegion stops working. It must be the setRegion because the correct lat/long are still printed. I also know it is not due to the annotations because the same bug happens when the annotation add/remove part is commented out. Does it possibly have something to do with linking the searchRequest.region and map.region? manager is the CLLocationManager instance, map is the MapView
func recenter(){
print("Recenter called")
guard let center = manager.location?.coordinate else{
print("Could not get location for recenter")
return
}
let newRegion = MKCoordinateRegion(center: center, latitudinalMeters: 1000, longitudinalMeters: 1000)
print("\(center.latitude) and \(center.longitude)")
map.setRegion(newRegion, animated: true)
}
func queryAndAnnotate(){
let searchRequest = MKLocalSearch.Request()
searchRequest.naturalLanguageQuery = "nightlife"
searchRequest.region = map.region
let search = MKLocalSearch(request: searchRequest)
search.start{response, error in
guard let response = response else {
Alert(title:Text("Alert"), message: Text("Error: \(error?.localizedDescription ?? "Unknown Error")"))
return
}
var mapItemPlacemarks: [MKPointAnnotation] = []
self.barsList = []
for i in response.mapItems{
let x = MKPointAnnotation()
x.coordinate = i.placemark.coordinate
x.title = i.name
mapItemPlacemarks.append(x)
}
self.map.removeAnnotations(mapItemPlacemarks)
self.map.addAnnotations(mapItemPlacemarks)
}
}
MKLocalSearch.start calls the completionHandler in background task.
In this handler you are updating your mapView instance in background task.
Make sure to call all update methods of mapView in main task (removeAnnotations, addAnnotations, setRegion)
btw in
self.map.removeAnnotations(mapItemPlacemarks)
self.map.addAnnotations(mapItemPlacemarks)
you are removing mapItemPlacemarks that do not exist in the mapView. This makes no sense, but it is not your problem.
As it turns out the problem was coming from a dynamic update to a ScrollView. The ScrollView was within a slide up drawer that sat above the MapView on the Z axis. When the ScrollView was updated with the results from the local search, the MapView object lost track of userLocation (read 0,0) and the region could not be programmatically changed. I ended up fixing the problem by ditching the recenter() function altogether and instead implementing an MKUserTrackingButton within its own UIViewRepresentable (for SwiftUI). The MKUserTrackingButton is how the system maps cycles through user tracking modes. You can read the docs for it here: https://developer.apple.com/documentation/mapkit/mkusertrackingbutton

Can't show the directions in iPhone's inbuilt Map application

This is my custom location set inside the iOS 10.0 iPhone 6 and iOS 11.2 iPhone 6 simulators.
And this is the code by which I am trying to open the iPhone's inbuilt Apple Map and show the directions from user's (above set) current location to the provided destination locations.
let regionDistance: CLLocationDistance = 1000
let coordinates = CLLocationCoordinate2D(latitude: 26.025860999999999, longitude: 56.089238999999999)
let regionSpan = MKCoordinateRegionMakeWithDistance(coordinates, regionDistance, regionDistance)
let options = [MKLaunchOptionsMapCenterKey: NSValue(mkCoordinate: regionSpan.center),
MKLaunchOptionsMapSpanKey: NSValue(mkCoordinateSpan: regionSpan.span),
MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving] as [String : Any]
let placemark = MKPlacemark(coordinate: coordinates, addressDictionary: nil)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = "Federal Electricity & Water Authority"
mapItem.openInMaps(launchOptions: options)
but this is not working for at all. It's showing me the error like below,
Note:
I have given the location access permission to the default Map
application.
All 4 MKLaunchOptionsDirectionsModeKey is not showing
the direction.
Tested it on the Simulator and a real device and it is not working in either place.
Setting destination coordinates like this doesn't help. let coordinates = CLLocationCoordinate2D(latitude: 26.025861, longitude: 56.089239)
Please help!
As Indrajeet commented, the directions in iPhone's Apple Map is limited to the countries which are listed in this URL.
So in my case, UAE is not listed currently.
Will update this answer in future once UAE will be listed.

How to get Apple Maps App (not MapKit) to add an annotation?

It's quite easy to add an annotation to a MapKit view which is inside your app.
theMap: MKMapView!
let pa = MKPointAnnotation()
pa.title = "title!!" etc
theMap.addAnnotation(pa)
But how the heck do you make the Maps App add an annotation??
Here's exactly how to open the Maps App to a certain address..
func appleMapApp() {
quickAddressText = "123 Smith St 90210"
let bottomText = "This shows at BOTTOM Of Maps App screen."
let g = CLGeocoder()
g.geocodeAddressString(quickAddressText) { placemarks, error in
if let found = placemarks?.first, let lok = found.location {
let p = MKPlacemark(coordinate: lok.coordinate, addressDictionary: nil)
let mapItem = MKMapItem(placemark: p)
mapItem.name = bottomText
mapItem.openInMaps(launchOptions: showing(location: lok))
}
}
}
// convenient function...
func showing(location: CLLocation, meters m: CLLocationDistance = 1000)->[String : Any]? {
let cr = MKCoordinateRegionMakeWithDistance(location.coordinate, m,m)
let options = [
MKLaunchOptionsMapCenterKey: NSValue(mkCoordinate: cr.center),
MKLaunchOptionsMapSpanKey: NSValue(mkCoordinateSpan: cr.span)
]
return options
}
That's great - but how the heck do you pass in a MKPointAnnotation to the actual Maps app??
(Again - it's easy to add a MKPointAnnotation on a map view inside your own app.)
Any ideas? Is it actually possible??
I think openInMaps limits you only to the five possible launch options. But I wonder if you could get Apple Maps to open in the original way, openURL and maps.apple.com/maps, as shown in this SO question. With the newest iOS versions, though, it seems you also need to register the URL you're using in your info.plist under "URL Types ... URL Schemes" or "LSApplicationQueriesSchemes". You might be able to pass an annotation as a parameter with the URL.

How can I use myLocation with Swift using Google Map SDK for iOS?

I am making a single view application in Xcode, with Google Map SDK. I have followed instructions online and my application can successfully load the google map view. I have also enabled myLocation, so that myLocation button shows on the map view.
I understand that clicking the myLocation button will change the camera location automatically, but I'm wondering what I should do to use the data of myLocation (say to add a marker or add a path node)?
I've tried directly accessing mapView.myLocation, for example
let lat = mapView.myLocation?.coordinate.latitude
let long = mapView.myLocation?.coordinate.longitude
path.addCoordinate(CLLocationCoordinate2D(latitude: lat!, longitude: long!))
However, this will crash the applicaton and throw:
fatal error: unexpectedly found nil while unwrapping an Optional value
What does this error message mean and how should I resolve this?
The error says that myLocation property of mapView is nil. You should check if there is a value before accessing it.
if let myLocation = mapView.myLocation {
let lat = myLocation.coordinate.latitude
let long = myLocation.coordinate.longitude
path.addCoordinate(CLLocationCoordinate2D(latitude: lat, longitude: long))
}
Also verify why myLocation is nil. It might be that the user didn't allow location services.
This will not give any errors:
let lat = mapView.myLocation?.coordinate.latitude ?? give any value of latitude
let long = mapView.myLocation?.coordinate.longitude ?? give any value of longitude

How to trigger an EventKit alarm with Coordinates in Swift

I don't know about anyone else, but EventKit seems to have very little in terms resources and tutorials online for you to refer to for help.
I need to trigger an alarm when a user hits a radius of a set of coordinates, I wasn't sure of the best ay to do this, I was torn between local notifications, and EventKit reminders.
I decided to go for eventKit as I felt that I could do more with more the alarms and it was the most practical way to do it however, having not known much about EventKit i had some issues.
Anyway I've managed to get together and build a sample project which works and triggers an alert when the user leaves their current location, the only problem is, is that I almost want to do the complete opposite of that, I want to trigger an alert when the user enters a set of coordinates, I assume that most of the code is transferrable, however I seem to be stuck on one bit mainly.
// Creates an EKStructuredLocation Instance with a title of "Current Location"
let location = EKStructuredLocation(title: "Current Location")
// Uses the last location update extracted from the locations array to supply you're current location
location.geoLocation = locations.last as! CLLocation
// Location is added to a newly created alarm instance
let alarm = EKAlarm()
alarm.structuredLocation = location
// This alarm is triggered when the user moves away from the location proximity
alarm.proximity = EKAlarmProximityLeave
stationReminder.addAlarm(alarm)
I'm struggling to find how to set the location of the alarm to coordinates rather than users location.
I tried changing this
location.geoLocation = locations.last as! CLLocation
to
location.geoLocation = CLCircularRegion(circularRegionWithCenter: CLLocationCoordinate2D(latitude: 37.33233141, longitude: -122.03121860), radius: 50.0, identifier: "Location1")
but this doesn't work, i believe i am on the right track but i am throwing up this error: Cannot assign a value of type 'CLCircularRegion!' to a value of type 'CLLocation!'
I've tried loads of things with no resolve, does anybody have any experience with this and know how to help?
I also assume i'll have to change the following from this
alarm.proximity = EKAlarmProximityLeave
to this
alarm.proximity = EKAlarmProximityEnter
UPDATE
I've taken on board some comments below and tried a bunch of other things to get this to work, I feel like I am so close but somethings just missing. I cannot get this alarm to trigger. Excuse all the code comments, it's just so you can see some of the attempts i have made at fixing this.
can anyone see anything wrong with this code for the alarm?
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
// Stops location manager from sending further updates
locationManager.stopUpdatingLocation()
// Creates a new EKReminder which is named and initialised with text from the UITextField
let stationReminder = EKReminder(eventStore: appDelegate!.eventStore)
stationReminder.title = locationText.text
// Stores the previously created EKReminder in the default calendar
stationReminder.calendar = appDelegate!.eventStore!.defaultCalendarForNewReminders()
// Creates an EKStructuredLocation Instance with a title of "Current Location"
let location = EKStructuredLocation(title: "Destination: Bournemouth Station")
// Uses the last location update extracted from the locations array to supply you're current location
// location.geoLocation = locations.last as! CLLocation
// location.geoLocation = CLCircularRegion(circularRegionWithCenter: CLLocationCoordinate2D(latitude: 37.33233141, longitude: -122.03121860), radius: 50.0, identifier: "Location1")
// location.geoLocation = CLLocation(latitude: 50.742771, longitude: -1.895072)
location.radius = 50.0
location.geoLocation = CLLocation(latitude:50.742771, longitude:-1.895072)
// location.radius = 10.0 // metres
// Location is added to a newly created alarm instance
let alarm = EKAlarm()
alarm.structuredLocation = location
// This alarm is triggered when the user moves away from the location proximity
// alarm.proximity = EKAlarmProximityEnter
alarm.proximity = EKAlarmProximityEnter // "geofence": we alarm when *arriving*
// but this will have no effect until Reminders is granted Location access...
// and in iOS 8 it won't even ask for it until it is launched
// also, in iOS 8 the separate background usage pref is withdrawn;
// instead, auth of Reminders for "when in use" covers this...
// ...because it means "this app *or one of its features* is visible on screen"
stationReminder.addAlarm(alarm)
// Now we have a fully configured reminder which we save in the Event Store
var error: NSError?
appDelegate!.eventStore?.saveReminder(stationReminder,
commit: true, error: &error)
if error != nil {
println("Reminder failed with error \(error?.localizedDescription)")
}
}
Looks like EKEvent has an EKStructuredLocation, which you are using correctly. However, you need to be careful of the type of the geoLocation property. It should be a CLLocation, which is not the same as a CLCircularRegion.
Steps to fix:
check the docs for EKStructuredLocation https://developer.apple.com/library/mac/documentation/EventKit/Reference/EKStructuredLocationClassRef/index.html
set the location.geoLocation to a CLLocation that you create from latitude, longitude coordinates. (check the docs for CLLocation: https://developer.apple.com/library/mac/documentation/CoreLocation/Reference/CLLocation_Class/index.html#//apple_ref/swift/cl/CLLocation)
geoLocation = CLLocation(latitude: 37.33233141, longitude: -122.03121860)
set the geoLocation.radius separately location.radius = 50.0
at that point setting proximityEnter should work as you expected

Resources