I am using google maps SDK for ios. I want to make a marker fixed in the center of the screen, so when user drags the map, the marker does not move and stays in the center. I am also trying to read the coordinate of the center after the drag. Any input would be appreciated. Thanks!
I would rather display view on top of GMSMapView (do not use markers for that). Since you have screen position for map view, that should be easy to place your view in correct position.
To get coordinates you can use mapView.projection.coordinateForPoint
Documentation is here
To know that drag is finished, make you view controller (or any other object) delegate of map view (GMSMapViewDelegate) and implement mapView:idleAtCameraPosition: method.
Docs are here
Create outlet of GMSMapView and attached Image at the center of the map and search bar at the top to present the location name.
Drag the map on Device, which will trigger 2 delegate method of GMSMapViewDelegate.
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition)
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition)
/**
* Called repeatedly during any animations or gestures on the map (or once, if the camera is
* explicitly set). This may not be called for all intermediate camera positions. It is always
* called for the final position of an animation or gesture.
*/
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
print("didchange")> >
returnPostionOfMapView(mapView: mapView)
}
/**
* Called when the map becomes idle, after any outstanding gestures or animations have completed (or
* after the camera has been explicitly set).
*/
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
print("idleAt")
//called when the map is idle
returnPostionOfMapView(mapView: mapView)
}
//Convert the location position to address
func returnPostionOfMapView(mapView:GMSMapView){
let geocoder = GMSGeocoder()
let latitute = mapView.camera.target.latitude
let longitude = mapView.camera.target.longitude
let position = CLLocationCoordinate2DMake(latitute, longitude)
geocoder.reverseGeocodeCoordinate(position) { response , error in
if error != nil {
print("GMSReverseGeocode Error: \(String(describing: error?.localizedDescription))")
}else {
let result = response?.results()?.first
let address = result?.lines?.reduce("") { $0 == "" ? $1 : $0 + ", " + $1 }
self.searchBar.text = address
}
}
}
Github Demo Project
Related
I have a marker that I want to have more of a slide effect than a lift and drop, as I'm using it to measure distances. When a marker is lifted, by default, googleMaps moves the map slightly upward as an effect to show the marker is lifted. is there a way to turn that movement of the map itself off?
let camera = GMSCameraPosition.camera(withTarget: holeLocation, zoom: 18.0, bearing: holeH, viewingAngle: 0)
self.gMapView.camera = camera
self.gMapView.mapType = .satellite
self.gMapView.settings.scrollGestures = true
self.gMapView.settings.rotateGestures = false
self.gMapView.settings.zoomGestures = true
let centerMarker = GMSMarker(position: self.holeCenterCoordinates)
centerMarker.icon = UIImage(named: "mapDragCircle.png")
centerMarker.map = self.gMapView
centerMarker.isDraggable = true
centerMarker.isFlat = true
Edit: I tried to set the GMSMapViewDelegate, and then create the following functions:
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
self.newCamera = gMapView.camera
}
func mapView(_ mapView: GMSMapView, didBeginDragging marker: GMSMarker) {
self.gMapView.camera = newCamera
}
func mapView(_ mapView: GMSMapView, didDrag marker: GMSMarker) {
self.gMapView.camera = self.newCamera
}
In order to 1. grab the current position of the map. 2. when the marker is selected, make the position of the map the idle position of the map, aka don't move the map! this piece works. the problem is the marker is still moved. I obviously can't constrain the marker's position at 'didDrag' (while it's dragging, it needs to be in motion), and at didbeginDragging, the markers position is still = the markers position before being moved.
something causes it to move in between didBeginDragging and didDrag, and I don't have a good way to counteract it.
I need to get the lat/long of the location where user taps on the google map view. Is there a necessity to open GooglePlacePickerViewController for this? I need to achieve this in the GMSMapView which is used to show user current location.
It is simple use Delegate method
First set delegate
self.mapView.delegate = self
Then just
extension YourViewController:GMSMapViewDelegate {
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
// USER TAP ON coordinate
}
}
Suggestion : Just go through this tutorial https://medium.freecodecamp.org/how-you-can-use-the-google-maps-sdk-with-ios-using-swift-4-a9bba26d9c4d will not take more than 20 min that my promise will conver all the basic stuff. :)
If you need points of view based system. for example if you want what is exact point where user tap according to view X, Y
Then
you can do it like this
let points:CGPoint = mapView.projection.point(for:coordinates)
Bingo !!
assign delegate like this.
mapView.delegate = self
And use GMSMapViewDelegate to use below method
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
print("You tapped at Location \(coordinate.latitude), \(coordinate.longitude)")
}
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.)
}
Please observe the linked image (bottom, below)
When a user moves the map screen to where the pin ends up near the edge of the screen and then selects that pin annotation, an Apple method then moves the map screen slightly toward the center of the screen so that the pin annotation callout is visible.
My question is twofold:
I. What Apple method is called to make that screen adjustment?
II. What would be the smartest way to implement that same functionality within the mapView didSelect view method if the MKMapViewDelegate extension were implemented?
Mapview screen adjustment sequence:
Theoretically, you can get the frame of the annotation view and check if it's too close to the edges of the map view:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
// if view.frame is too close to the edges of the MKMapView, triggered your desired method
}
In practice this is unreliable because it's also depends on the callout bubble of the annotation view. We need a diffrent threshold for the "too close to the edges" criteria depending on the size of the callout bubble, for which there's no easy way to do.
This is a little hacky: when the screen adjusts, the regionWillChangeAnimated method will be called. If it is triggered within a split second of tapping on an annotation, there's a good chance that it was caused by user tapping on the annotation:
weak var tappedAnnotationView: MKAnnotationView?
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
self.tappedAnnotationView = view // this property will only be non-nil for 0.1 seconds
DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) {
self.tappedAnnotationView = nil
}
}
func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
if self.tappedAnnotationView != nil {
print("map adjusted due to tap on an annotation view")
}
}
It works for the basic tests I threw at it, but obviously there will be extreme cases when it break down since it's a hack.
What I came up with is the following:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
// Get Coordinate for Selected Map Pin
let selectedPinCoordinate = (view.annotation?.coordinate)!
// Determine (X,Y) coordinates for Selected Map Pin on view (mapView)
let SelectedPinMapPoint = mapView.convert(selectedPinCoordinate, toPointTo: mapView)
// Determine (X,Y) coordinate for center point of view (mapView)
let mapCenterMapPoint = mapView.convert(mapView.centerCoordinate, toPointTo: mapView)
// Define an inner view within the existing view frame. Any Selected Map Pin outside
// bounds of inner view is deemed to be too close to the edge of the map for the callout
// to be fully viewable by the user
let innerRectFrameMinX = mapView.frame.maxX * 0.15
let innerRectFrameMaxX = mapView.frame.maxX * 0.85
let innerRectFrameMinY = mapView.frame.maxY * 0.30
let innerRectFrameMaxY = mapView.frame.maxY * 0.85
// Create a variable which will serve as the new center (X,Y) coordinate
var newCenter = CGPoint()
// Default new center (X,Y) coordinate to current view (X,Y) coordinate
newCenter.x = mapCenterMapPoint.x
newCenter.y = mapCenterMapPoint.y
// Determine if x coordinate of Selected Map Pin is outside bounds. If so,
// set a new center x coordinate so callout can be clearly seen by the user
switch (SelectedPinMapPoint.x) {
case _ where (SelectedPinMapPoint.x < innerRectFrameMinX):
newCenter.x = mapView.frame.midX - (innerRectFrameMinX - SelectedPinMapPoint.x)
case _ where (SelectedPinMapPoint.x > innerRectFrameMaxX):
newCenter.x = mapView.frame.midX + (SelectedPinMapPoint.x - innerRectFrameMaxX)
default: break
}
// Determine if y coordinate of Selected Map Pin is outside bounds. If so,
// set a new center y coordinate so callout can be clearly seen by the user
switch (SelectedPinMapPoint.y) {
case _ where (SelectedPinMapPoint.y < innerRectFrameMinY):
newCenter.y = mapView.frame.midY - (innerRectFrameMinY - SelectedPinMapPoint.y)
case _ where (SelectedPinMapPoint.y > innerRectFrameMaxY):
newCenter.y = mapView.frame.midY + (SelectedPinMapPoint.y - innerRectFrameMaxY)
default: break
}
// Convert new map Center (X,Y) coordinate to map coordinate
let newCenterCoordinate = mapView.convert(newCenter, toCoordinateFrom: nil)
// Set new center as center for map view
mapView.setCenter(newCenterCoordinate, animated: true)
}
I've linked a rough graphical sketch of my conception for whatever benefit it may provide.Rough Graphical Sketch
What i want is I want to fix the marker position to the centre of my map view. when map moves still my marker should stick to that centre point.
Code for marker is shown below:
var pin:GMSMarker = GMSMarker(position: CLLocationCoordinate2DMake(currentLocation.coordinate.latitude, currentLocation.coordinate.longitude))
pin.icon = UIImage(named: "img_map_pin")
pin.map = mapView?
pin.infoWindowAnchor = CGPointMake(0.44, 0.44)
mapView?.selectedMarker = pin
I want to make same as Uber has done. When map moves marker is in centre and after map stops moving I get that point's address from map.
Assign GMSMapViewDelegate to class and add below code
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
pin.position = position.target
}