didTapInfoWindowOfMarker on iOS App Swift - ios

Right now my app has multiple markers on different locations. If you tap on a marker, there is a small window popping out including a title and a snippet. I would like to implement a button in the window, or make the info window tappable, so it works as a button to execute a function. So I wrote this block in my GoogleMapsViewController.swift:
func mapView(mapView: GMSMapView, didTapInfoWindowOfMarker marker: GMSMarker) {
print("test")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("jobDetailVC") as! JobDetailViewController
if let value = marker.userData as? PFObject {
vc.name = value.objectForKey("name") as? String
vc.descriptionF = value.objectForKey("description") as? String
vc.price = value.objectForKey("price") as? Double
vc.objectId = value.objectId!
}
}
The reason why I am using: didTapInfoWindowOfMarker is because I wasn't sure how to implement it, so I read the docu from Google Maps: https://developers.google.com/maps/documentation/ios-sdk/reference/protocol_g_m_s_map_view_delegate-p?hl=es and thought that this was the best choice.
Has anyone successfully implemented this, or something similar? Thanks in advance for the help!

You are right in using didTapInfoWindowOfMarker function to add an event in your InfoWindow.
When you're adding Map, add:
mapView_.delegate=self;
Then use this to add the event/function of infoWindow when clicked:
-(void)mapView:(GMSMapView *)mapView
didTapInfoWindowOfMarker:(id<GMSMarker>)marker{
//Info window function
}
Example on GitHub:
// when user tap the info window of store marker, show the product list
func mapView(mapView: GMSMapView!, didTapInfoWindowOfMarker marker: GMSMarker!) {
let storeMarker = marker as StoreMarker
performSegueWithIdentifier("productMenu", sender: storeMarker.store)
}
// when user tap the info window of store marker, pass selected store to the product list controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let controller = segue.destinationViewController as ProductMenuController
controller.store = sender as Store
}
func mapView(mapView: GMSMapView, didTapInfoWindowOfMarker marker: GMSMarker) {
for location in locations {
let pollution = location[0]
if pollution.locationdesc == marker.title {
performSegueWithIdentifier(segueIdentifiers.locations, sender: location)
break
}
}
}
Check this related question:
Adding Click Event on InfoWindow/Marker in Google Maps SDK for native iOS/objective C

Related

How to identify if MKPointAnnotation has been pressed in Swift?

Using a for loop I have stored a unique URL in each annotation using a tag create in a class named CustomPointAnnotation. I am trying to print out the URL of the annotation that has been pressed. The problem is my output console in Xcode prints nothing when I click on an annotation.
I tried to follow this guide: How to identify when an annotation is pressed which one it is
I replicated all the code but it is not detecting if the annotation was clicked.
How do I know if the annotation is clicked?
Here is the CustomPointAnnotation.
class CustomPointAnnotation: MKPointAnnotation {
var tag: String!
}
I declared the variable tag so I can store a unique variable for each annoation.
My ViewController class:
In the ViewController class there is a loop which iterates through my Firebase Database JSON files:
func displayCordinates() {
ref = Database.database().reference()
let storageRef = ref.child("waterfountains")
storageRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children.allObjects as! [DataSnapshot] {
let annotation = CustomPointAnnotation()
let dict = child.value as? [String : AnyObject] ?? [:]
annotation.title = "Water Fountain"
annotation.tag = dict["url"] as? String
annotation.coordinate = CLLocationCoordinate2D(latitude: dict["lat"] as! Double, longitude: dict["long"] as! Double)
self.mapView.addAnnotation(annotation)
}
})
}
The annotations are displayed by calling the functions in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
displayCordinates()
}
A function which should detect if an annotation has been clicked:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if let annotation = view.annotation as? CustomPointAnnotation {
print(annotation.tag!)
}
}
Thank you for helping.
mapView:didSelect: is a MKMapViewDelegate method. If you do not set mapView.delegate = self on your ViewController this function will never get triggered.
Typically it would get set in ViewDidLoad. before performing any other operations with the mapView. Changing your ViewDidLoad to look like
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self
displayCordinates()
}
should fix your issue. For more information on the protocol/delegate design pattern all over the apple frameworks, I suggest this swift article from the Swift Programming Guide.
More specifically to your case, check out all the other functionality/control you can bring with your implementation of MKMapView on your ViewController by checkout out the apple docs on MKMapViewDelegate. This will cover things like monitoring when the map finishes loading, when it fails, when the user's location is updated, and more things you may want to increase the functionality of your app and provide a great user experience.

How do we retrieve PlaceID if I have place coordinate info on IOS Swift

I am using GoogleMaps and GooglePlaces APIs. I am trying to retrieve place details when I tap a marker. I understand I can retrieve place detail by using a placeID, but how do I retrieve a PlaceID in the first place? I have the coordinates of the place.
There is information on how to use a PlaceID once we have it, but there isn't much information on how to retrieve a PlaceID. I have place coordinate. Either I could use those coordinates directly to get details of the place or use it to get Place ID and then use the PlaceID to retrieve place Details.
Any ideas?
You can use the userData section of the marker.
To define userData,
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let customData = customData(var123: "", var1234: 4, var12345: true)
marker.userData = customData
marker.map = self.mapView
}
To retrieve the information use,
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
print(marker.title)
// print marker userData
print(marker.userData)
print(CLLocation(latitude: marker.layer.latitude, longitude: marker.layer.longitude))
return true
}
Finally you can define the customData class,
class customData{
var var1: String
var var2: Int
var var3: Bool
init(var123: String, var1234: Int, var12345: Bool) {
var1 = var123
var2 = var1234
var3 = var12345
}
}

iOS Swift - How to wait end of request before return Info Window (Google Maps)

I am working with Google Maps and custom Info Window.
I have an issue where I need to update a label but the data comes from a success callback.
The problem here is that when the data is available I have already returned my Info Window and I can't update it anymore because it is already rendered (snapshot of the view).
func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? {
let infoWindow = Bundle.main.loadNibNamed("CustomInfoWindow", owner: self, options: nil)?.first as! CustomInfoWindow
let obs = marker.userData as! Observation
ObsService.shared.getCityFromLatLong(lat: String(obs.coordinate.latitude), long: String(obs.coordinate.longitude)) { response in
infoWindow.outletPlaceLabel.text = response
}
infoWindow.setDateAndTime(timestamp: obs.timestamp)
return infoWindow
}
I know I have several options such as :
Notification
Wait until request is done (blocking UI?)
Schedule my task
Init method with logic instead of loadNibNamed only
I begin developing with Swift 3.
EDIT: It's working with a sleep(2). But I CAN'T block the UI every time I need to display an info window.
I can't find an other solution.
var cityName: String?
func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? {
if cityName == nil {
let obs = marker.userData as! Observation
ObsService.shared.getCityFromLatLong(lat: String(obs.coordinate.latitude), long: String(obs.coordinate.longitude) { response in
self.cityName = response
// Replace the existing map view with a new one here. Or call it's refresh function if it has one (I don't know, haven't used Google map view)
return
}
}
return self.mapViewHelper(mapView, markerInfoWindow: marker, city: cityName ?? "")
}
func mapViewHelper(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker, city: String?) -> UIView? {
let infoWindow = Bundle.main.loadNibNamed("CustomInfoWindow", owner: self, options: nil)?.first as! CustomInfoWindow
let obs = marker.userData as! Observation
infoWindow.outletPlaceLabel.text = response
infoWindow.setDateAndTime(timestamp: obs.timestamp)
return infoWindow
}
If you have set the map view initially using Interface builder, you can give it a tag, and then search for the view with the relevant tag, instantiate a new map view where I have written the comment, and replace the existing map view (copying its frame from the existing map view). The map view may also have a refresh function you can call. See the documentation for views for how to search for views with tags (sorry if that's teaching a grandmother to suck eggs!)
Try this :
let infoWindow:CustomInfoWindow? // declare out side of method
Your function Now
func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? {
infoWindow = Bundle.main.loadNibNamed("CustomInfoWindow", owner: self, options: nil)?.first as! CustomInfoWindow
let obs = marker.userData as! Observation
ObsService.shared.getCityFromLatLong(lat: String(obs.coordinate.latitude), long: String(obs.coordinate.longitude)) { response in
infoWindow.outletPlaceLabel.text = response
}
infoWindow.setDateAndTime(timestamp: obs.timestamp)
return infoWindow
}

Passing data from Marker to other VC

At the beggining of this post I would like thank #Nirav D for help :)
Ok. So the problem.
There is a class i want to pass with prepareForSegue func with google marker. How to do it?
You are too close to pass simply set the passedMarker with your array object.
if let marker = sender as? GMSMarker , //is it correct do declare marker as GMSMarker?
let dict = marker.userData as? [String:Int] {
let markerIndex = dict["index"]!
nextVC.passedMarker = self.markers[markerIndex]
}
Now simply access passedMarker property in viewDidLoad of VC3.
Edit this
performSegue(withIdentifier: "details", sender: marker)
To
performSegue(withIdentifier: "details", sender: self)

How to add custom data to overlay in Google Maps for iOS?

How can i add custom data to GMSGroundOverlay images in Google Maps for iOS? I am successfully passing data on markers when clicked with the built-in "marker.userData" but the ground overlays dont have that property?
Okay i figured this out if it's helpful to anyone else. Not sure if this is the best way to do this, but it works.
1.) I extended the GMSGroundOverlay class in the map's main .swift file
extension GMSGroundOverlay {
private struct customData {
static var userData:Any? = nil
}
var userData:Any? {
get {
return customData.userData
}
set {
customData.userData = newValue
}
}
}
2.) I pass the data on the overlay before overlays are added to map
let overlayData:[String:Any] = ["id":id,"html":html,"picture":picture,"photos":photos]
overlay.userData = overlayData
3.) I receive the data when overlays are clicked
func mapView(_ mapView: GMSMapView, didTapOverlay overlay: GMSGroundOverlay) -> Bool {
print("# You tapped overlay with data: ")
for (key,value) in overlay.userData as! [String:Any] {
print("# \(key) = \(value)")
}
return false
}

Resources