Creating buttons for a popup(Speech Bubble) in Mapbox using Swift - ios

I posted another question with the same code, but this question is different.
I want to add buttons in the bottom right corner of my Speech bubble that displays
Hello World!
Welcome to my marker!
I want to know how to place the buttons there, but if you want to know what the buttons would do, one of them would keep track of how many upvotes the bubble got by other users, and the other would send a request to another user.
Also, I found this example that looks like it implements a different version of a speech bubble(popup) that may be better to use
import Mapbox
class ViewController: UIViewController, MGLMapViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let mapView = MGLMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Set the map’s center coordinate and zoom level.
mapView.setCenter(CLLocationCoordinate2D(latitude: 40.7326808, longitude: -73.9843407), zoomLevel: 12, animated: false)
view.addSubview(mapView)
// Set the delegate property of our map view to `self` after instantiating it.
mapView.delegate = self
// Declare the marker `hello` and set its coordinates, title, and subtitle.
let hello = MGLPointAnnotation()
hello.coordinate = CLLocationCoordinate2D(latitude: 40.7326808, longitude: -73.9843407)
hello.title = "Hello world!"
hello.subtitle = "Welcome to my marker"
// Add marker `hello` to the map.
mapView.addAnnotation(hello)
}
// Use the default marker. See also: our view annotation or custom marker examples.
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
return nil
}
// Allow callout view to appear when an annotation is tapped.
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
return true
}
}
Below is what I would like my expected output to look like approximately

If you want to use the built-in Mapbox callout you may want to consider implementing the -mapView:rightCalloutAccessoryViewForAnnotation:
delegate method that allows you to further customize an MGLCallout as illustrated in this example: https://www.mapbox.com/ios-sdk/maps/examples/default-callout/. That delegate method returns a UIView so you can customize the UIView however you'd like to contain the buttons you want.
You'll notice in the example that another delegate method, -mapView:annotation:calloutAccessoryControlTapped: is also implemented. This gets called when the right callout accessory view (returned by -mapView:rightCalloutAccessoryViewForAnnotation:) is selected, so you could adapt this by placing your logic in that delegate method when a user selects the right side of the callout view.

Related

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

Moving Google Maps Info Window in iOS

So I have a custom info window that appears whenever a user taps a pin in Google Maps. However, I want the info window to appear at the bottom of the screen when the user taps a pin and not above the pin as is default. Basically, when you tap the pin, the map centers on the pin as it does normally but I want the info window to appear at the very bottom of the screen, above the Google Maps logo. I'm currently using this function to show the custom info window:
func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) ->
UIView? {
let infoWindow = Bundle.main.loadNibNamed("customInfoWindow", owner: self, options: nil)?.first! as! customInfoWindow
infoWindow.title.text = marker.title
return infoWindow
}
According to the post above, i think you need to add a custom Label rather that the default info window. Implement the below delegate method.
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
// Add a Label on the Map at the desired position,
// get the coordinates of the marker from 'marker.position'
// use the coordinates for displaying it in the label or rev geo to get the address and display (according to the required spec.)
}

Mapbox iOS SDK - visibleFeaturesAtPoint returns empty array

I am trying the MGLMapView.visibleFeaturesAtPoint but am always getting back an empty array. Can someone tell me what I am doing wrong here?
Posted below is my code which is basically the adding the marker example (https://www.mapbox.com/ios-sdk/examples/marker/) but using the same point to get visible features at the marker point.
import Mapbox
class ViewController: UIViewController, MGLMapViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let mapView = MGLMapView(frame: view.bounds)
mapView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
// Set the map’s center coordinate and zoom level.
mapView.setCenterCoordinate(CLLocationCoordinate2D(latitude: 40.7326808, longitude: -73.9843407), zoomLevel: 12, animated: false)
view.addSubview(mapView)
// Set the delegate property of our map view to `self` after instantiating it.
mapView.delegate = self
// Declare the marker `hello` and set its coordinates, title, and subtitle.
let hello = MGLPointAnnotation()
hello.coordinate = CLLocationCoordinate2D(latitude: 40.7326808, longitude: -73.9843407)
hello.title = "Hello world!"
hello.subtitle = "Welcome to my marker"
// Add marker `hello` to the map.
mapView.addAnnotation(hello)
//let ptTest = CGPoint(x: 1, y: 1)
print(mapView.visibleCoordinateBounds)
let ptTest = mapView.convertCoordinate(hello.coordinate, toPointToView: mapView)
print(ptTest)
print(mapView.visibleFeatures(at: ptTest))
}
// Use the default marker. See also: our view annotation or custom marker examples.
func mapView(mapView: MGLMapView, viewForAnnotation annotation: MGLAnnotation) -> MGLAnnotationView? {
return nil
}
// Allow callout view to appear when an annotation is tapped.
func mapView(mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
return true
}
}
Question was answered by mapbox team on Github.
"Does the issue reproduce if you move the call to visibleFeatures(at:) to viewDidAppearAnimated(:) or mapViewDidFinishLoadingMap(:)? By the time viewDidLoad() runs, the view controller has loaded, but the map view may not have had a chance to load the style or tiles completely yet."
Apparently putting it in viewDidLoad() meant that the map had not fully loaded yet and so the features array was returned empty. Moving it to mapViewDidFinishLoadingMap() fixed the issue.

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.

Creating a dynamic subview in swift iOS Map app

This is my first foray into the app world so it's taken a lot of research to get to this point. I'm building a map application and am going for an interface similar to the zillow app seen below. I am trying to come up with the right approach that allows me to click on a map annotation and it brings up a smaller view where I can interact with it. So essentially I have a few questions:
Should I use a subview inside the map controller, or use a container view. Or is there another approach I haven't seen?
How do I push data from the annotation to that function?
How do I keep this subview hidden until an annotation has been clicked?
So far this is the closest thing I can find: Customize MKAnnotation Callout View?
Thanks!
I am new to iOS also, but I have done something similar to what you want to do. I have a view that shows some statistics, speed, bearing, etc. When someone clicks on the annotation, I toggle showing and hiding the statistics. There may be better ways, but here is what I did.
"Executive summary for your questions"
1 and 3) Use a subview over the map that you hide and unhide
2) Subclass both MKAnnotation and MKAnnotationView. Put the data you want to pass in a property of the subclassed MKAnnotationView, and then transfer the property to the MKAnnotationView when you create it. You can then retrieve it from the view passed in to didSelectAnnotationView.
Details
1) and 3) I created a subview that sits on the mapView and set it as hidden in the story board initially. I then have a toggeleMarkerStatistics() func
tion that toggles the visibility of the view. So something like this
func toggleMarkerStatistics() {
if mapMarkerStatistics.hidden {
mapMarkerStatistics.hidden = false
} else {
mapMarkerStatistics.hidden = true
}
}
This function is called from within
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) { }
2) To get data into the didSelectAnnotationView, here is what I did.
I subclassed both MKAnnotation and MKAnnotationView and added properties to hold the data that I wanted to pass to didSelectAnnotationView. So something like this:
class MyAnnotation: MKPointAnnotation {
var myMarker: MyMapMarker?
}
class MyMKAnnotationView: MKAnnotationView {
var myMarker: MyMapMarker?
}
When you create the annotation, set the property, before you add the annotation to the map.
let annotation = MyAnnotation()
annotation.myMarker = marker
annotation.coordinate = location
annotation.title = "btw, you MUST have a title or bad things happen"
mapView.addAnnotation(annotation)
Then in viewForAnnotation, you will be given your custom annotation with the property you set after you created it and you are asked to create a view for this annotation. Now when you create this view, set the view property to annotation property before you return the view. Now the data you want to pass to didSelectAnnotationView will be available on the view passed to didSelectAnnotationView
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let view: MKAnnotationView! = mapView.dequeueReusableAnnotationViewWithIdentifier("marker") ?? MyMKAnnotationView(annotation: annotation, reuseIdentifier: "marker")
view.annotation = annotation
view.canShowCallout = false // you need this to make this work
if let annotation = annotation as? MyAnnotation {
if let view = view as? MyMKAnnotationView {
view.myMarker = annotation.myMarker
}
}
return view
}
Now in didSelectAnnotationView, retrieve the data you set when you created the annotation:
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
if let view = view as? MyMKAnnotationView {
if let marker = view.myMarker {
toggleMarkerStatistics() // hide or unhide the view
// do something with your data
}
}
}
Note:
I tried to copy and simplify this from my code, which actually tries to support both Apple and Google maps, so hopefully I don't have any typo's, but I think is a good representation of the steps I take.
A few more things to note:
I think you must provide a title for the annotaion
I think you must set the view's canShowCallout to false
I think both of these requirements can be found in the documentation, but I don't have a pointer to this right now.

Resources