Mapbox iOS - Show multiple callouts - ios

I want to show multiple annotations at once in Mapbox (note: NOT MKMap). There doesn't seem to be a method to show multiple annotations, although it can add multiples.
e.g.
mapView.addAnnotations(<annotationsArray>)
Even though there is a method as follows, it does not show the annotations.
mapView.showAnnotations(<annotationsArray>, animated: true)
What I want it to show multiple instances as shown in the image below. i.e. I want to show several "Hello World" annotations in the map, for each point the user added.
Is there a similar method in Mapbox to show multiple annotations in Swift?

Use this to display multiple annotations:
func setAnnotations() {
var index = 0
for placeModel in self.placesList! {
let marker = MyMGLPointAnnotation()
marker.willUseImage = true
marker.id = placeModel.id
marker.type = placeModel.type
if let img = placeModel.images.first {
marker.imgURL = img.thumbnail
}
marker.coordinate = CLLocationCoordinate2D(latitude: placeModel.location?.latitude ?? 0, longitude: placeModel.location?.longitude ?? 0)
marker.title = placeModel.name.withoutHtmlTags
marker.subtitle = placeModel.descr.htmlToString
//DispatchQueue.main.async {
self.mapView.setCenter(CLLocationCoordinate2D(latitude: latitude, longitude: longitude), zoomLevel: 12, animated: false)
self.mapView.addAnnotation(marker)
}
}
You can display multiple instance of the annotations but for the selection you can tap one by one and it will show title like the image you shared.
Edit: There is way you can create your custom xib load as annotation to show the title.
Check: https://github.com/mapbox/ios-sdk-examples/blob/0e2c8ce878de500f36c4168f7a1e62041c8adbdf/Examples/Swift/AnnotationViewsAndImagesExample.swift
https://docs.mapbox.com/ios/maps/examples/annotation-view-image/

Related

Here iOS SDK creating draggable marker

I want to create a draggable marker but it doesn't work. What am I missing?
let marker = NMAMapMarker(geoCoordinates: coordinates, image: markerImage!)
marker.isDraggable = true
mapView.add(mapObject: maker)
mapView.respond(to: .markerDragBegan) { (drag, map, marker) -> Bool in
return true
Please take a reference from below sample code. Also share the error logs that you got while using above code snippet.
/**
* create a NMAMapMarker object, then add it to current active map view.
*/
- (void)addMapMarker {
//create NMAImage with local cafe.png
NMAImage* markerImage = [NMAImage imageWithUIImage:[UIImage imageNamed:#"cafe.png"]];
//create NMAMapMarker located with geo coordinate and icon image
NMAMapMarker* mapMarker = [NMAMapMarker mapMarkerWithGeoCoordinates:self.mapView.geoCenter icon:markerImage];
//make marker able to receive dragging gesture from map
mapMarker.draggable = YES;
//add NMAMapMarker to map view
[self.mapView addMapObject:mapMarker];
//add view and handlers for *MarkerDrag* events:
[self setupMapMarkerEventHandlersIfNeeded];
[_mapMarkers addObject:mapMarker];
}

GMSMarker icon in the top left corner of the view (iOS)

I am trying to create a UITableViewCell containing a GMSMapView with a GMSMarker at the center of the current Position.
The problem is that the marker always appears at the top left corner of the current position and I don't know how to solve the problem.
I tried to follow these steps: Implementing a Google Map with UItableviewCell
here is my code from cellForRowAt:
let locationCell = tableView.dequeueReusableCell(withIdentifier: "activityLocationCell") as! ActivityLocationCell
let latitude = CLLocationDegrees(activity.coordinates![0])
let longitude = CLLocationDegrees(activity.coordinates![1])
let position = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
locationCell.googleMapView.camera = GMSCameraPosition.camera(withTarget: position, zoom: 15)
let marker = GMSMarker(position: position)
marker.groundAnchor = CGPoint(x: 0.5, y: 0.5)
marker.map = locationCell.googleMapView
return locationCell
Here is a screenshot of my problem:
marker is at the top left corner of the map
I had a pretty similar issue. I resolved it by changing the moment I configure the map in the view lifecycle.
In my case, I was using a child view controller. I was configuring the map before viewWillAppear was called which caused the map to not center properly (the marker was on the top left corner). I moved my call to after the viewWillAppear and it fixed it. A good place would be viewDidAppear.
If you are using a cell, you will probably need to investigate with the view lifecycle instead of the controller lifecycle.
This is not written anywhere on the Google documentation.
you have to draw map in
func viewDidLayoutSubviews()
Try creating Marker when map is ready completely. for eg: use the delegate.
var ifMapReady: Bool = false
...
...
func mapViewSnapshotReady(_ mapView: GMSMapView) {
ifMapReady = true
}
//Call this method from where ever you want to load map
func updateMap() {
if ifMapReady {
//Load Map
}
}
This delegate will be called multiple times(eg: map is swiped or moved etc) whenever the Map tiles are ready. So we can use a boolean value for understanding that map loaded successfully. Based on that value we can load the Map properly when initiating.
I want to add one more thing. #Gabriel Cartier's answer worked for me with one additional change in my code.
[self->mapView_ animateToCameraPosition:camera];
And I replaced with
[self->mapView_ setCamera:camera];

Get nearby places from Google API sorted by distance

I am currently stuck on this functionality where user need nearby places results on the basis of distance.
For e.g :-
If i search "Kochi" and i am in India
Then kochi is in India as well as in Japan
So result should be like
1. Kochi, India
2. Kochi, Japan
This is just an example, user can also search landmarks, city, streets etc.. But i want all results to be sorted by distance.Closer results will display first and then far places. But not able to get results according to requirement.
Similar functionality is done on android and they are using it by passing radius (Like 500 km from current location)
What i have tried so far :-
Using GMSPlacesClient.autocompleteQuery and passing bounds in it for current location
GMSPlacesClient().autocompleteQuery(txtLocation.text!, bounds: bounds, filter: filter, callback: {(results, error) -> Void in
if let error = error {
print("Autocomplete error \(error)")
return
}
if let results = results {
}
})
Using GooglePlaces.placeAutocomplete
GooglePlaces.placeAutocomplete(forInput: self.txtLocation.text!, offset: 0, locationCoordinate: nil, radius: nil, language: nil, types: [GooglePlaces.PlaceType.Regions], components: nil, completion: { (response, error) in
// To do
})
I also used this url (https://maps.googleapis.com/maps/api/place/autocomplete/json?input=pan&location=30.704316,76.712106&radius=50000&components=country:IN) for Google API but for these kind of alternatives i have to do custom parsing.
I am playing around with the same GoogleMaps API. I do the same as you requested but through a different way code is attached, I do this on a button press for 'Search' you can either set the bounds to coordinates or the map view frame. In the below it is currently using the users current location for the bounds and it then works its way out from that I think this works near enough perfectly:
let autoCompleteController = GMSAutocompleteViewController()
autoCompleteController.delegate = self as! GMSAutocompleteViewControllerDelegate
// Set bounds to map viewable region
let visibleRegion = googleMaps.projection.visibleRegion()
let bounds = GMSCoordinateBounds(coordinate: visibleRegion.farLeft, coordinate: visibleRegion.nearRight)
// New Bounds now set to User Location so choose closest destination to user.
let predictBounds = GMSCoordinateBounds(coordinate: userLocation.coordinate, coordinate: userLocation.coordinate)
autoCompleteController.autocompleteBounds = predictBounds
// Set autocomplete filter to no filter to include all types of destinations.
let addressFilter = GMSAutocompleteFilter()
addressFilter.type = .noFilter
autoCompleteController.autocompleteFilter = addressFilter
// Change text color
UISearchBar.appearance().setTextColor(color: UIColor.black)
self.present(autoCompleteController, animated: true, completion: nil)
I am having a problem with Google Directions and getting the calculated distance and miles if you have any code for this that may be of help for me!

Set first annotation in annotations

OK,
I have an array of pubs:
var pubs = [Pub(coordinate: CLLocationCoordinate2D(latitude: 43.2282385, longitude: 76.855085)),
Pub(coordinate: CLLocationCoordinate2D(latitude: 43.2345965, longitude: 76.8907758)).
and I add annotations to my map:
mapView.addAnnotations(pubs).
Then I select first annotation this way:
mapView.selectAnnotation(mapView.annotations.first!, animated: true)
But now it shows first selected annotations, then second.
Question:
How to set the needed annotation first in annotations?
Thanks.
Howe said #ogres: mapView might re-arrange the array of the Pubs, so i had to select it from the TableView-s array mapView.selectAnnotation(tableView.annotations.first!, animated: true)

Swift - how to add custom fields to markers in Google Maps SDK?

I try to build app with maps, and I want to have additional info in markers. Documentation offers only 2 properties title and snippet. It looks like this
let position = CLLocationCoordinate2DMake(51.5, -0.127)
let london = GMSMarker(position: position)
london.title = "London"
london.snippet = "Population: 8,174,100"
london.map = mapView
For example, I want to add field rating to each marker to display average rating for place.
How can I do that ?
The Google Maps SDK includes a userData field on the marker object, see link.
Using your example this is what it would look like the following to assign the data.
let mCustomData = customData(starRating: 10) // init with a rating of 10
let position = CLLocationCoordinate2DMake(51.5, -0.127)
let london = GMSMarker(position: position)
london.title = "London"
london.snippet = "Population: 8,174,100"
london.userData = mCustomData
london.map = mapView
And the customData class something like this.
class customData{
var starRating: Int
init(starRating rating: Int) {
starRating = rating
}
}
To access the your user data just retrieve it from the marker object.
let mRating = (london.userData as! customData).starRating

Resources