moving MapView programmatically - ios

I have a mapView in which I show a location (represented by custom pin) as seen in screenshot
How can I move the mapView so that the icon is visible completely?

To achieve this you first need to calculate the point on screen view where you want to scroll (In your case let assume its 20 pixel up to the annotation marker), Then you need to covert this point to Map location so that you can move map center to that location ;-). Following is the code written in Swift in MKMapView delegate methods where annotation gets tapped.
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
let pinLocation = view.annotation.coordinate
let currentCoordinates = mapView.centerCoordinate // Temporary saved map current center position
// Temp set map center position to pin location
mapView.centerCoordinate = pinLocation
let viewCenter = self.view.center
let fakecenter = CGPoint(x: viewCenter.x, y: viewCenter.y - 20) // point just up to the center point
// convert calculetd point to map location co-ordinates
let coordinate: CLLocationCoordinate2D = mapView.convert(fakecenter, toCoordinateFrom: self.view)
// reset to previous potion so thet animation start from that
mapView.centerCoordinate = currentCoordinates
self.mapView.setCenter(coordinate, animated: true) // change the new center
}

Related

iOS - Animate to a location while showing all the markers in GMSMapView

I want to animate to a specific location while showing all the markers i have pinned on Google map .
I am able to animate to a location but it is not showing all the markers.
I have tried the following solution (answer link) .
I am calling hover(to location:) for each location i want to animate:
func viewAllMarkers() {
var bounds = GMSCoordinateBounds()
for marker in yourArrayOfMarkers {
bounds = bounds.includingCoordinate(marker.position)
}
let update = GMSCameraUpdate.fit(bounds, withPadding: 60)
mapView.animate(with: update)
}
func hover(to location: CLLocationCoordinate2D) {
viewAllMarkers()
mapView?.animate(toLocation: location)
}
But it does not work . It animates to a location but does not show all the markers in GMSMapView . How to do both at the same time ?

iOS -Adding and Removing MKCircle Overlay to MapView causing glitch

I have a MapView and the user can select a radius to show an area. I add a MKCircle as an overlay. When the radius changes from 30 miles to 1 mile there is a noticeable glitch on the perimeter of the MKCircle while the MapView zooms in. The glitch looks sort of like a flair. It only happens when zooming in and not zooming out.\
Since the zoom constantly changes I remove the old overlay before adding another one but I don't think that's the issue.
How can I remove the glitch on the circle as the MapView's zoom changes?
#IBAction func newRadiusButtonTapped(sender: UIButton) {
// coordinate is the users location and span was 10 miles now it's 1 mile
let region = MKCoordinateRegionMake(location.coordinate, span)
mapView.setRegion(region, animated: true)
let circle = MKCircle(center: location.coordinate, radius: radius)
// remove old overlay before adding another one
for overlay in mapView.overlays {
mapView.remove(overlay)
}
view.layoutIfNeeded()
// mapView.layoutIfNeeded() I tried this but it didn't make a difference
mapView.add(circle)
}
I couldn't find what was causing the glitch but I found this delegate method from this answer on the mapView that gets notified after the mapView region finishes changing. I add the overlay there
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
}
Simple process:
I create a circle property of type MKCircle?
I also create property named shouldAddCircle of type Bool and set it to true.
When the button is pressed I initial the circle property with the MKCircle that I create inside the button and set the shouldAddCircle to true.
Inside the button function I remove all the mapViews overlays.
Inside the delegate method I now check to see that the shouldAddCircle property is true and if it is I then check to see make sure the circle property isn't nil. If they match then I add the initialized circle to the mapView. After I add the circle to the mapView I have to set the shouldAddCircle to false because every time the user scrolls the map regionDidChangeAnimated gets called and it will keep adding overlays to the map.
Here' the code below. Be sure to add mapView.delegate = self in viewDidLoad and to set the MKMapViewDelegate before everything.
var circle: MKCircle?
var shouldAddCircle = true
#IBAction func newRadiusButtonTapped(sender: UIButton) {
// coordinate is the users location and span was 10 miles now it's 1 mile
let region = MKCoordinateRegionMake(location.coordinate, span)
mapView.setRegion(region, animated: true)
let circle = MKCircle(center: location.coordinate, radius: radius)
// set the circle property to match the circle that was just created
self.circle = circle
// set this true
shouldAddCircle = true
// remove old overlay before adding another one
for overlay in mapView.overlays {
mapView.remove(overlay)
}
}
// this function gets called repeatedly as the mapView is zoomed and/or panned
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
// make sure this is true because that means the user updated the radius
if shouldAddCircle {
// make sure the circle isn't ni
if let circle = self.circle {
// after the mapView finishes add the circle to it
mapView.add(circle)
// set this to false so that this doesn't called again until the user presses the button where they set it to true
shouldAddCircle = false
}
}
}

How to animate Google map marker smoothly from one location to another location in ios

i Have a car icon in google map and which have to be moved in regular time interval. the location coordinates will be fetched from server and i have managed to change the location of marker by doing the code below but didint got a smooth movement
let camera = GMSCameraPosition.camera(withLatitude: (((self.driverArrival.value(forKey: "latitude")) as AnyObject).doubleValue)!,
longitude: (((self.driverArrival.value(forKey: "longitude")) as AnyObject).doubleValue)!, zoom: 15)
let position = CLLocationCoordinate2DMake((((self.driverArrival.value(forKey: "latitude")) as AnyObject).doubleValue)!, (((self.driverArrival.value(forKey: "longitude")) as AnyObject).doubleValue)!)
driverMarker.position = position
driverMarker.map = self.mapView
here driverdetails contains all the required data. Just want to know i can use any animation functions to achieve this?
You can use animate(to: GMSCameraPosition) to update map position with animation an example will look like this :-
func updateMapLocation(lattitude:CLLocationDegrees,longitude:CLLocationDegrees){
let camera = GMSCameraPosition.camera(withLatitude: lattitude, longitude: longitude, zoom: 16)
mapView?.camera = camera
mapView?.animate(to: camera)
}
and call the method like this
updateMapLocation(lattitude:-33.8683,longitude:151.2086)
For more information
Edit
For marker position update you can use a single marker and update its position with this code
CATransaction.begin()
CATransaction.setAnimationDuration(2.0)
marker.position = coordindates // CLLocationCoordinate2D coordinate
CATransaction.commit()
Please Don't use GMSCameraPosition for move pin in the map
You can use mapView.animate(toLocation: YOUR_COORDINATES) method to smoothly move pin in the map
self.mapView.animate(toLocation: CLLocationCoordinate2D(latitude: YOUR_LATITUDE, longitude: YOUR_LONGITUDE))
self.marker.position = coordinate
self.marker.map = self.mapView
Hope this helps!
Try this ...
Add GMSMapViewDelegate to your class,
self.mapView.delegate = self //Call delegate
//MARK - MapView delegates
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
self.marker.map = mapView;
self.marker.position = position.target //Your target position
self.mapView.selectedMarker = self.marker //Your marker
DispatchQueue.main.async {
self.marker.snippet = "Getting address... " //Your snippet title
}
}
func mapView(_ mapView:GMSMapView, idleAt position:GMSCameraPosition) {
//Get address with village names and street names
self.getAddressForLatLng(latitude: "\(position.target.latitude)", longitude: "\(position.target.longitude)", zoomLevel: position.zoom)
}

Swift - Making MapView callout annotation visible when pin annotation clicked by User

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

Is there a way to Fix a Google Maps Marker to the Center of its Map always in iOS using swift?

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
}

Resources