How to make a MGLPolyline tapable? - ios

I am currently looking for a way to make a route (MGPLPolyline) tappable such that a user can choose between two possible routes under Mapbox. Unfortunately the route annotation doesn't seem to be tappable at all:
While i have properly set my MGLMapViewDelegate this method:
func mapView(_ mapView: MGLMapView, didSelect annotation: MGLAnnotation)
is never invoked.
What did i miss knowing that each line inherited from MGLPolyline which is a subclass of an MGLAnnotation (which i was expected to be tappable...).

The Mapbox iOS API doesn't support that yet, unfortunately. See here.
There are a couple of workarounds within that link that you could try though.
UPDATE:
This is now possible: Check here.

You could do the following:
First, when you create the polyline do the following:
let polyline = CustomPolyline(coordinates: &coordinates, count: UInt(coordinates.count))
polyline.title = "" //It does not seem to matter what you set it to.
polyline.color = .darkGray
Then in the following method return false:
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
// Always allow callouts to popup when annotations are tapped.
print("ran?")
return false
}

Related

Mapbox: Refreshing an annotationView when the annotation changes

I have custom annotations that sometimes display a textView above them.
They don't display a textView if a variable named text on my annotation is nil.
An annotation may have text to display, but the value of the text variable could change while the annotation is being displayed. In this case I would like the annotation to refresh so that it is no longer displaying the textView.
I already have a delegate function that either creates an annotation with a textView if the annotations text variable is set and creates an annotation without a textView if the text variable of the annotation is not set, it works something like this, although this is not the actual code
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView?{
if annotation is MyCustomAnnotation{
if annotation.hasText(){
return MyCustomAnnotationView(hasText: True)
}else{
return ViewWithoutTextView(hasText: False)
}
}
But if the annotation changes from having text to not having text or vice versa while the annotation is already being displayed, then I don't know how to refresh this or call this again so that the right annotation view is displayed
As #Magnas said in the comment, you would have to remove the annotation and re-add it to update the state.
It would be better to create one custom annotation view that has the logic to handle hiding/showing of the text view inside it. Then you just hold onto a reference of the annotation and update that through the annotationView without going through and messing with map annotations at all.
A rough example (lots of blanks to fill):
// your methods in your custom annotation. Use these wherever you want to change things
class CustomAnnotation: MGLAnnotationView {
func showText() { }
func hideText() { }
}
// Define data structure to access your annotation with some kind of key
dataSourceToAnnotationView: [String: CustomAnnotation]
// save your annotations so you can access them later
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "customReuseId")
if annotationView == nil {
annotationView = CustomAnnotation()
let key = "exampleKeyString"
dataSourceToAnnotationView[key] = annotationView as! CustomAnnotation
}
return annotationView
}

How to zoom, in MGL mapView, so that the view encompasses one "object" on the map

I have an MGLPolyline on a mapbox map and want to make is so when the user taps on the line it centers around the line and zooms in as much as possible so that the full line is on display. Currently, I make the centering work well but the zoom works randomly:
I just set it to max zoom, However, this is of course not what I want.
Bellow is where I want to add the zoom amount:
func mapView(_ mapView: MGLMapView, didSelect annotation: MGLAnnotation) {
print("Tapped")
mapView.setCenter(CLLocationCoordinate2D(latitude: annotation.coordinate.latitude, longitude: annotation.coordinate.longitude), zoomLevel: mapView.zoomLevel, animated: true)
mapView.deselectAnnotation(annotation, animated: false)
}
MGLMapView actually has a baked in method specifically for this purpose. You should be able to implement the functionality using -showAnnotations:animated. If you want to fiddle around with the padding around your polyline, you can also use the showAnnotations:edgePadding:animated flavor of the method.
This would look like the following:
func mapView(_ mapView: MGLMapView, didSelect annotation: MGLAnnotation) {
mapView.showAnnotations(pointAnnotations, animated: true)
}

How can I change mapbox annotation view?

I've add several annotations with custom annotation view, but I have a reference only to the marker and not to the annotation view itself. So how can I change the annotation view elements?
You should store the annotations in a variable and when you want to change their views just remove them from the map with mapView.removeAnnotation(...) and add it back again with mapView.addAnnotation(...).
The method
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
}
will be called and you should return your changed view.
Pay attention to the fact that for performance reasons you should not use addAnnotation() or removeAnnotation() if you have a lot of annotations, but instead addAnnotations() and removeAnnotations()
I added all my annotation views to a dictionary
I created a global dictionary variable
var annotationViews = [CustomAnnotation:CustomAnnotationView]()
Then after creating each annotation view within my delegate function
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView?
I added it into my dictionary
let annotationView = MyAnnotationView(reuseIdentifier: rid, size: CGSize(width: 45, height: 45), annotation: annotation)
annotationViews[annotation as! MyAnnotation] = annotationView
Later when I wanted to change the text of a text view within my annotation view, I used my annotation in the dictionary call. This returned the annotation view that corresponded to it.
self.annotationViews[anno]?.textView.text = "My new text view status"
No deleting and re-adding required.
You could probably insert something smaller as the key for the dictionary, like annotation.uid or annotation.name or something. I just used the whole annotation object though

UIButton, Mapbox, Open new View programmatically

I am a beginner, and i can't understand one thing.
In this example there exist Info button. As I understand there piece of code returns this button:
func mapView(_ mapView: MGLMapView, rightCalloutAccessoryViewFor annotation: MGLAnnotation) -> UIView? {
return UIButton(type: .detailDisclosure)
}
So the question is: how can I open another View (view name: extraInfo, viewcontroller's name: extraInfoController) from this button programmatically?
Please, can u reply in detail, because as i already sad, I am beginner. :)
(I am using Swift, not Obj-C)
You can use this delegate method
func mapView(_ mapView: MGLMapView, annotation: MGLAnnotation, calloutAccessoryControlTapped control: UIControl) {
}

Mapbox - setCenterCoordinate is deselecting annotations

I'm trying out mapbox (using the ios sdk) and I've run into a problem that I think I've narrowed down pretty far. This is my code:
func centerMap(location: CLLocationCoordinate2D) {
map.setCenterCoordinate(location,
zoomLevel: 14,
animated: true)
}
func mapView(mapView: MGLMapView, didDeselectAnnotation annotation: MGLAnnotation) {
dealDetails.hidden = false
}
func mapView(mapView: MGLMapView, didUpdateUserLocation userLocation: MGLUserLocation?) {
if let currentLocation = userLocation?.coordinate {
centerMap(currentLocation)
}
}
If I don't re-center the map when the user's location is updated (i.e., just commenting out the centerMap(currentLocation) call) then the annotation remains selected. Re-centering the map calls the didDeselectAnnotation function, and I can't figure out how to keep that annotation selected. Any help is appreciated!
I don't think there's any way around that if you update the center coordinate. You'd have to re-select the annotation. However, you probably don't need to do that. If you set the userTrackingMode on the map view to .Follow, it should re-center automatically.

Resources