How to update a GMSMapView with GMSCameraUpdate - swift

I call animateWithCameraUpdate on a GMSMapView expecting it to change the map view to show the new GMSCoordinateBounds but it has no effect.
My map loads in a UICollectionViewReusableView to initially display Western Europe:
#IBOutlet weak var mapView: GMSMapView!
var fetchedResultsController : NSFetchedResultsController!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
let camera = GMSCameraPosition.cameraWithLatitude(51.48, longitude: 0, zoom: 4)
mapView.camera = camera
}
I then call a function to query all my locations and update the GMSMapView to show all my locations:
func plotAll(){
let bounds = GMSCoordinateBounds.init()
for property in fetchedResultsController.fetchedObjects!
{
let capitalAsset : CapitalAsset = property as! CapitalAsset
let marker = GMSMarker.init()
marker.draggable = false
marker.snippet = capitalAsset.address
let location = CLLocationCoordinate2DMake(Double(capitalAsset.latitude!), Double(capitalAsset.longitude!))
marker.position = location
marker.map = mapView
// Update bounds to include marker
bounds.includingCoordinate(marker.position)
}
mapView.animateWithCameraUpdate(GMSCameraUpdate.fitBounds(bounds, withPadding: 50.0))
}
My plotAll function is successfully called and loops through a dozen global locations adding these to the GMSCoordinateBounds.
But the map is not being updated. I was expecting the map view to change when I called animateWithCameraUpdate but it has no effect.
Further information, for debugging I replaced the line
mapView.animateWithCameraUpdate(GMSCameraUpdate.fitBounds(bounds, withPadding: 50.0))
with :
let camera = GMSCameraPosition.cameraWithLatitude(51.48, longitude: 0, zoom: 10)
mapView.camera = camera
This does update my map view so there is no problem with calling my plotAll function, the issue is probably in my use of animateWithCameraUpdate.

I got this to work by replacing :
mapView.animateWithCameraUpdate(GMSCameraUpdate.fitBounds(bounds, withPadding: 50.0))
with:
let camera = mapView.cameraForBounds(bounds, insets:UIEdgeInsetsZero)
mapView.camera = camera;

Related

Google Maps iOS SDK GMSURLTileLayer displaying incorrect color/transparency

PNG Tiles are showing incorrect color and transparency on iOS. Transparent areas show up as a semi-transparent white. The same tile set shows up correctly on Android and web browser, so I don't believe it is an issue is with the tiles themselves. Here's my Swift code:
class ViewController: UIViewController {
#IBOutlet weak var mapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
let camera = GMSCameraPosition.camera(withLatitude: 30.00, longitude: -90, zoom: 7.0)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
view = mapView
// Implement GMSTileURLConstructor
let urls: GMSTileURLConstructor = { (x, y, zoom) in
let url = "https://aerocast.s3.amazonaws.com/radar/klix/lixtileskml/\(zoom)/\(x)/\(y).png"
return URL(string: url)
}
let tilelayer = GMSURLTileLayer(urlConstructor: urls)
tilelayer.zIndex = 100
tilelayer.map = mapView
tilelayer.opacity = 1.0
}
}
I've looked for some way to adjust the color properties of the tilelayer, but have not been able to find anything.
The issue was specific to the iOS emulator on xcode. The issue is not present on a physical iOS device. I brought up the problem to Google Maps support and they were able to replicate the problem and are looking into it.
I think there is some problem with view hierarchy
class ViewController: UIViewController {
#IBOutlet weak var mapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
let camera = GMSCameraPosition.camera(withLatitude: 30.00, longitude: -90, zoom: 7.0)
self.mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
self.view.addSubview(mapView)
// Define constraints (i.e position in the parent view), example for full screen ones
mapView.translatesautoresizingmaskintoconstraints = false
mapView.topAnchor.constraint(equalToSystemSpacingBelow: view.topAnchor).isActivate = true
mapView.bottomAnchor.constraint(equalToSystemSpacingBelow: view.bottomAnchor).isActivate = true
mapView.leftAnchor.constraint(equalToSystemSpacingBelow: view.leftAnchor).isActivate = true
mapView.rightAnchor.constraint(equalToSystemSpacingBelow: view.rightAnchor).isActivate = true
// Implement GMSTileURLConstructor
let urls: GMSTileURLConstructor = { (x, y, zoom) in
let url = "https://aerocast.s3.amazonaws.com/radar/klix/lixtileskml/\(zoom)/\(x)/\(y).png"
return URL(string: url)
}
let tilelayer = GMSURLTileLayer(urlConstructor: urls)
tilelayer.zIndex = 100
tilelayer.map = mapView
tilelayer.opacity = 1.0
}
}

How to set/update zoom level to GMSMapView in Swift?

Following code is how I got new marker position and update mapview.
if self.state.dropOff != nil {
let loc = response
let position = CLLocationCoordinate2D(latitude: loc.latitude!, longitude: loc.longitude!)
self.getPolylineRoute(from: self.state.pickUp!.coordinate, to: self.state.dropOff!.coordinate)
CATransaction.begin()
CATransaction.setAnimationDuration(1.0)
if self.acceptedCabMarker == nil {
self.acceptedCabMarker = GMSMarker(position: position)
}
self.acceptedCabMarker!.position = position
self.acceptedCabMarker!.isFlat = true
self.acceptedCabMarker!.icon = UIImage(named: markerIcon)
self.acceptedCabMarker!.setIconSize(scaledToSize: .init(width: 40, height: 40))
self.acceptedCabMarker!.appearAnimation = .pop
self.acceptedCabMarker!.rotation = CLLocationDegrees(loc.bearing ?? 0)
CATransaction.commit()
DispatchQueue.main.async {
self.acceptedCabMarker!.map = self.mapView
}
}
Problem is everytime this code is executed, mapview zoom level became to its original state. Which mean user can't zoom the map for long.
I tried to save the zoom using method.
extension SomeHomeViewController: GMSMapViewDelegate {
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
print("Camera Zoom: \(position.zoom)")
currentPosition = position
}
}
But I can't reuse currentPosition because
self.mapView?.camera.zoom = currentPosition?.zoom
is not allowed.
You can simply set the zoom on GMSMapView in this way
let camera = GMSCameraPosition.camera(withLatitude: loc.latitude, longitude: loc.longitude, zoom: 10.0)
self.mapView.camera = camera
You need to use
- (void)animateToZoom:(float)zoom;
method which is defined in GMSMapView (Animation) Category. For more info you can refer this link.

Not able to add Multiple Markers in GSMapView xCode Swift 3

I am trying to add multiple Markers on Googlemaps in my app.
In the viewcontrolller under viewDidLoad I am able to load the map and a single marker.
override func viewDidLoad() {
super.viewDidLoad()
title = NSLocalizedString("section_map", comment: "test")
let camera = GMSCameraPosition.camera(withLatitude: 48.7784, longitude:9.18121, zoom: 12)
let mapView = GMSMapView.map(withFrame: .zero, camera: camera)
view = mapView
mapView.settings.myLocationButton = true
mapView.delegate = self
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: 48.7784,longitude: 9.18121)
marker.title = "title"
marker.snippet = "snipple"
marker.icon = UIImage(named:"pin_you")
marker.map = mapView
mapData()
}
It call mapData() and from there is json file is generated
after parsing setPin is called to set the markers
func setPin(){
DispatchQueue.main.async {
for item in self.mapItems {
print (" \(item.name) \(item.marker) \(item.latitude) \(item.longitude)")
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: item.latitude,longitude: item.longitude)
marker.title = item.name
marker.snippet = item.fulladdress
var iconImage: String
switch (item.marker){
case 1:
iconImage = "pin_silver"
case 2:
iconImage = "pin_blue"
case 3:
iconImage = "pin_gold"
case 6:
iconImage = "pin_you"
default:
iconImage = "pin_silver"
}
marker.icon = UIImage(named:iconImage)
marker.map = self.mapView
}
}
}
The pins are not shown.
The print in for item in self.mapItems shows
Position number A 1 48.76947562 9.15440351
Position number B 1 48.75716485 9.17081058
Position number C 1 48.81191625 9.22752149
Position number D 2 48.81192516 9.22766708
this means all the proper data is available.
However the map is there the one pin made in viewDidLoad
The Markers in function setPin are not shown or maybe not set.
Does any-one have an idea?
I have solved it in have changed the mapData(mapView: GMSMapView!) also for setPin(mapView: GMSMapView!)
func setpin(mapView: GMSMapView!){
DispatchQueue.main.async {
for item in self.mapItems {
print (" \(item.name) \(item.marker) \(item.latitude) \(item.longitude)")
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: item.latitude,longitude: item.longitude)
marker.title = item.name
marker.snippet = item.fulladdress
var iconImage: String
switch (item.marker){
case 1:
iconImage = "pin_silver"
case 2:
iconImage = "pin_blue"
case 3:
iconImage = "pin_gold"
case 6:
iconImage = "pin_you"
default:
iconImage = "pin_silver"
}
marker.icon = UIImage(named:iconImage)
marker.map = mapView
}
}
}
Clustering your markers, you can put a large number of markers on a map without making the map hard to read. The marker clustering utility helps you manage multiple markers at different zoom levels.
For the full code sample, see the ObjCDemoApp and SwiftDemoApp on GitHub.
To add a simple marker clusterer.
/// Point of Interest Item which implements the GMUClusterItem protocol.
class POIItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var name: String!
init(position: CLLocationCoordinate2D, name: String) {
self.position = position
self.name = name
}
}
The following code creates a cluster manager using the GMUNonHierarchicalDistanceBasedAlgorithm and the MUDefaultClusterRenderer that are included in the utility library:
class ViewController: UIViewController, GMUClusterManagerDelegate,
GMSMapViewDelegate {
private var mapView: GMSMapView!
private var clusterManager: GMUClusterManager!
override func viewDidLoad() {
super.viewDidLoad()
// Set up the cluster manager with the supplied icon generator and
// renderer.
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView,
clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm,
renderer: renderer)
// Generate and add random items to the cluster manager.
generateClusterItems()
// Call cluster() after items have been added to perform the clustering
// and rendering on map.
clusterManager.cluster()
}
}
Feed your markers into the cluster as GMUClusterItem objects by calling clusterManager:addItem:. The following code randomly generates cluster items (POIs) within the scope of the map's camera, then feeds them to the cluster manager:
/// Randomly generates cluster items within some extent of the camera and
/// adds them to the cluster manager.
private func generateClusterItems() {
let extent = 0.2
for index in 1...kClusterItemCount {
let lat = kCameraLatitude + extent * randomScale()
let lng = kCameraLongitude + extent * randomScale()
let name = "Item \(index)"
let item =
POIItem(position: CLLocationCoordinate2DMake(lat, lng), name: name)
clusterManager.addItem(item)
}
}
/// Returns a random value between -1.0 and 1.0.
private func randomScale() -> Double {
return Double(arc4random()) / Double(UINT32_MAX) * 2.0 - 1.0
}
Handle events on markers and clusters
class ViewController: UIViewController, GMUClusterManagerDelegate, GMSMapViewDelegate {
private var mapView: GMSMapView!
private var clusterManager: GMUClusterManager!
override func viewDidLoad() {
super.viewDidLoad()
// ... Rest of code omitted for easy reading.
// Register self to listen to both GMUClusterManagerDelegate and
// GMSMapViewDelegate events.
clusterManager.setDelegate(self, mapDelegate: self)
}
// MARK: - GMUClusterManagerDelegate
func clusterManager(clusterManager: GMUClusterManager, didTapCluster cluster: GMUCluster) {
let newCamera = GMSCameraPosition.cameraWithTarget(cluster.position,
zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
}
// MARK: - GMUMapViewDelegate
func mapView(mapView: GMSMapView, didTapMarker marker: GMSMarker) -> Bool {
if let poiItem = marker.userData as? POIItem {
NSLog("Did tap marker for cluster item \(poiItem.name)")
} else {
NSLog("Did tap a normal marker")
}
return false
}
}
For more Details please follow the Here

screen not zoom to right coordinates - swift google map api

when getting dynamic google map with using GMSMap the map doesn't show the current location that i gave him and not zoom the map
import UIKit
import GoogleMaps
import MapKit
class LocationItemViewController: UIViewController,GMSMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var lblInfo: UILabel!
#IBOutlet weak var lblTitle: UILabel!
#IBOutlet weak var viewMap: UIView!
#IBOutlet weak var googleMapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
let latitude = (String(format:"%.02f", locationItem.itemLatitude ) as NSString).doubleValue
let longgitude = (String(format:"%.02f", locationItem.itemLongitude) as NSString).doubleValue
let camera = GMSCameraPosition.cameraWithLatitude(latitude,longitude: longgitude, zoom: 7)
let mapView = GMSMapView.mapWithFrame(CGRectZero, camera: camera)
mapView.myLocationEnabled = true
self.googleMapView = mapView
let marker = GMSMarker()
marker.position = CLLocationCoordinate2DMake((locationItem.itemLatitude as NSString).doubleValue, (locationItem.itemLongitude as NSString).doubleValue)
marker.title = "somelocation"
marker.snippet = "anywhere"
marker.map = mapView
lblTitle.text = locationItem.itemName
lblInfo.text = locationItem.itemAddress
}
I was locate the GMSServices.provideAPIKey("YOUR_API_KEY") in app delegate
To display your current location you have to do the same thing which we do for apple maps to access our location.
In plist you have to add 2 entries and take permission from User
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
Check this link for more details iOS 8 CLLocationManagerDelegate methods are never called
Now regarding your google maps zoom issue
var mapView:GMSMapView?
Now in your viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
GMSServices.provideAPIKey("YOUR_API_KEY")
//Taking HardCoded lat longs for the time being. These lat longs are of New Delhi, India
let camera = GMSCameraPosition.cameraWithLatitude(28.6139, longitude: 77.2090, zoom: 10)
mapView = GMSMapView.mapWithFrame(CGRectZero, camera: camera)
mapView!.myLocationEnabled = true
self.view = mapView
let marker = GMSMarker()
marker.position = CLLocationCoordinate2DMake(28.6139, 77.2090)
marker.title = "Delhi"
marker.snippet = "India"
marker.map = mapView
//As view takes some time to load so I am calling my zoom function after a delay of 1 second. You can use the zoom function code in viewDidAppear too
//Also this syntax is the latest Swift 2.2 syntax. If you are using the old Swift version, make the changes accordingly for performSelector method
self.performSelector(#selector(zoom), withObject: nil, afterDelay: 1.0)
}
Here is the zoom method
func zoom() {
CATransaction.begin()
CATransaction.setValue(1, forKey: kCATransactionAnimationDuration)
// It will animate your camera to the specified lat and long
let camera = GMSCameraPosition.cameraWithLatitude(28.6139, longitude: 77.2090, zoom: 15)
mapView!.animateToCameraPosition(camera)
CATransaction.commit()
}

Failed to set maprect to show all annotations

I have 2 annotations to display on the mapview, but unable to set the maprect to show all of them on screen without requiring users to zoom out.
I tried with showAnnotations but no luck. Anyone has been able to do this in Swift and Xcode 6.1.1?
Here is my code:
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet var map: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
var mapView = map
// 1
let point1 = CLLocationCoordinate2D(latitude: 38.915565, longitude: -77.093524)
let point2 = CLLocationCoordinate2D(latitude: 38.890693, longitude: -76.933318)
//2
let annotation = MKPointAnnotation()
annotation.setCoordinate(point1)
annotation.title = "point1"
map.addAnnotation(annotation)
let annotation2 = MKPointAnnotation()
annotation2.setCoordinate(point2)
annotation2.title = "point2"
map.addAnnotation(annotation2)
//3
// option1: set maprect to cover all annotations, doesn't work
var points = [annotation, annotation2]
var rect = MKMapRectNull
for p in points {
let k = MKMapPointForCoordinate(p.coordinate)
rect = MKMapRectUnion(rect, MKMapRectMake(k.x, k.y, 0.1, 0.1))
println("result: x = \(rect.origin.x) y = \(rect.origin.y)")
}
map.setVisibleMapRect(rect, animated: true)
// option 2: using showAnnotations, doesn't work
//map.showAnnotations(points, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This is what I got currently:
This is what I expected to see:
Thanks for your help.
I finally found out why the pins of the annotations had not been displayed in the visible region of the screen. I think the MapKit framework behaves a bit different than in the previous versions. Since I use autolayout to allow the map to expand to the entire screen for all devices (iPhones, iPad), setVisibleMapRect or mapView.showAnnotations of the map should be invoked in mapViewDidFinishLoadingMap, not in viewDidLoad of the view controller
For example:
func mapViewDidFinishLoadingMap(_ mapView: MKMapView) {
// this is where visible maprect should be set
mapView.showAnnotations(mapView.annotations, animated: true)
}
I had this same problem when I called
viewDidLoad() {
mapView.showAnnotations(myAnnotations, animated: false)
}
However, moving the call to viewDidLayoutSubviews() also seems to fix the problem (not that isInitialLoad is initialized to true in viewDidLoad).
viewDidLayoutSubviews() {
if isInitialLoad {
mapView.showAnnotations(myAnnotations, animated: false)
isInitialLoad = false
}
}
The difference (I think it is an advantage) of putting the call in viewDidLayoutSubviews is that the map hasn't actually displayed yet, so your initial display is that area defined by the annotations. However, it seems that it is called every time the map zooms, so you need to be sure to only call it the first time.
For me using showing annotations after map did finish loading did not work.
func mapViewDidFinishLoadingMap(mapView: MKMapView!) {
// this is where visible maprect should be set
mapView.showAnnotations(mapView.annotations, animated: true)
}
Besides showing the annotation, I needed to calculate polylines to connect the annotations and map finished loading was triggered too early.
Instead I tried mapViewDidFinishRenderingMap and it worked perfectly fine. See example below:
//MARK: - Show all objects after adding them on the map
func mapViewDidFinishRenderingMap(mapView: MKMapView, fullyRendered: Bool) {
mapView.showAnnotations(mapStages, animated: true)
}
You can try this.
I created an extension to show all the annotations using some code from here and there in swift 2.3. This will not show all annotations if they can't be shown even at maximum zoom level.
import MapKit
extension MKMapView {
func fitAllAnnotations() {
var zoomRect = MKMapRectNull;
for annotation in annotations {
let annotationPoint = MKMapPointForCoordinate(annotation.coordinate)
let pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
zoomRect = MKMapRectUnion(zoomRect, pointRect);
}
setVisibleMapRect(zoomRect, edgePadding: UIEdgeInsetsMake(20, 20, 20, 20), animated: true)
}
}