How to add GMSMapView in Storyboard in IOS - ios

https://developers.google.com/maps/documentation/ios-sdk/utility/marker-clustering
I am working for map clustering . In map clustering
private var mapView: GMSMapView!
is used for mapView but couldn't find any GMSMapView! in storyboard connection .
From Storyboard i found
#IBOutlet var mapView: MKMapView!
that makes the problem . when i used exampled google map it throws error
this class is not key value coding-compliant for the key mapView.'
Here the complete code :
import UIKit
import MapKit
import GooglePlaces
import GoogleMaps
// 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
}
}
let kClusterItemCount = 10000
let kCameraLatitude = -33.8
let kCameraLongitude = 151.2
class FirstViewController: UIViewController , GMUClusterManagerDelegate,
GMSMapViewDelegate {
// #IBOutlet var mapView: MKMapView!
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.
// Set up the cluster manager with default 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()
// Register self to listen to both GMUClusterManagerDelegate and GMSMapViewDelegate events.
clusterManager.setDelegate(self, mapDelegate: self)
}
// override func loadView() {
//
// // Create a GMSCameraPosition that tells the map to display the
// // coordinate -33.86,151.20 at zoom level 6.
// let camera = GMSCameraPosition.camera(withLatitude: -33.86, longitude: 151.20, zoom: 6.0)
// let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
// view = mapView
//
// // Creates a marker in the center of the map.
// let marker = GMSMarker()
// marker.position = CLLocationCoordinate2D(latitude: -33.86, longitude: 151.20)
// marker.title = "Sydney"
// marker.snippet = "Australia"
// marker.map = mapView
//
// }
//
// MARK: - GMUClusterManagerDelegate
func clusterManager(clusterManager: GMUClusterManager, didTapCluster cluster: GMUCluster) {
let newCamera = GMSCameraPosition.camera(withTarget: 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
}
// MARK: - Private
/// 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.add(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
}
}

First Take UIView:
Add UIView In UIViewController:
Assign class GMSMap View
And Now create outlet of GMSMapView.
#IBOutlet var mapView: GMSMapView!

To add GMSMapView in Storyboard in IOS, open the Identity Inspector and under Custom Class add GMSMapView. You may follow this tutorial about Google Maps SDK for iOS in Xcode Storyboard for the step-by-step instructions. For your error this class is not key value coding-compliant for the key mapView, you might have created an outlet called mapview and deleted it later. Check whether the outlets of MapView is broken in Xib/Storyboard. Here are also some references which might help: https://www.raywenderlich.com/109888/google-maps-ios-sdk-tutorial and What does this mean? "'NSUnknownKeyException', reason: … this class is not key value coding-compliant for the key X"

Related

Polyline not showing in swiftUI

Hi everyone I've been working for a few days to show a straight line on my map. I use swiftUI and mapkit to render the map. what I want to achieve is a straight line between the two annotations, these are shown on the map.
Dit is de code die ik op dit moment heb. Ik hoop dut jullie mij kunnen helpen want ik kom er niet uit.
import MapKit
struct MapViewWorldDetail: UIViewRepresentable {
var StartCoordinate: CLLocationCoordinate2D
var EndCoordinate: CLLocationCoordinate2D
var name: String
#Binding var region: CLLocationCoordinate2D
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ view: MKMapView, context: Context) {
let span = MKCoordinateSpan(latitudeDelta: 50, longitudeDelta: 50)
let region = MKCoordinateRegion(center: self.region, span: span)
// view.mapType = MKMapType.satelliteFlyover;
view.setRegion(region, animated: true)
let annotation = MKPointAnnotation()
annotation.coordinate = StartCoordinate
annotation.title = name
view.addAnnotation(annotation)
let annotationEnd = MKPointAnnotation()
annotationEnd.coordinate = EndCoordinate
annotationEnd.title = name
view.addAnnotation(annotationEnd)
let aPolyline = MKGeodesicPolyline(coordinates: [StartCoordinate, EndCoordinate], count: 2)
view.addOverlay(aPolyline)
}
}
Note on the straight line you are drawing: The line that you are drawing using MKGeodesicPolyline has this note in the Apple Developer Documentation:
MKGeodesicPolyline - When displayed on a two-dimensional map view, the line segment between any two points may appear curved.
Example of working code
In SwiftUI, you'll need to implement the MKMapViewDelegate in a Coordinator class, which is where the Overlay handling is taken care of:
Add this to func updateUIView
func updateUIView(_ view: MKMapView, context: UIViewRepresentableContext<MapView>) {
// Stuff you already had in updateUIView
// :
// adding this at the end is sufficient
mapView.delegate = context.coordinator
}
Add this to your struct, struct MapViewWorldDetail
// MARK: - Coordinator for using UIKit inside SwiftUI.
func makeCoordinator() -> MapView.Coordinator {
Coordinator(self)
}
final class Coordinator: NSObject, MKMapViewDelegate {
var control: MapView
init(_ control: MapView) {
self.control = control
}
// MARK: - Managing the Display of Overlays
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
print("mapView(_:rendererFor:)")
if let polyline = overlay as? MKPolyline {
let polylineRenderer = MKPolylineRenderer(overlay: polyline)
polylineRenderer.strokeColor = .red
polylineRenderer.lineWidth = 3
return polylineRenderer
}
return MKOverlayRenderer(overlay: overlay)
}
}
Good references to review:
Sample project that implements the Coordinator class & delegates (but does not do overlays)
https://github.com/roblabs/ios-map-ui/tree/master/MapKit-SwiftUI-for-iOS-macOS
Medium posts by others
https://medium.com/better-programming/exploring-mapkit-on-ios-13-1a7a1439e3b6
https://medium.com/flawless-app-stories/mapkit-in-swiftui-c0cc2b07c28a

Custom Marker in Google Map in swift

I'm trying to show my custom view on click of pin in google map. I have mad my custom view for it in xib file and call that file in the delegate method of func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool { But when i run the app and tap the marker it does not show my view. How can i show this? This is my code for custom Xib file,
class MapMarkerWindow: UIView {
#IBOutlet weak var saloonImage: UIImageView!
#IBOutlet weak var nameLbl: UILabel!
#IBOutlet weak var addressLbl: UILabel!
#IBOutlet weak var descriptionLbl: UILabel!
var spotData: NSDictionary?
class func instanceFromNib() -> UIView {
return UINib(nibName: "MapMarkerWindowView", bundle: nil).instantiate(withOwner: self, options: nil).first as! UIView
}
}
This is the code in my VC where i'm showing my map,
#IBOutlet weak var customView: UIView!
var mapView : GMSMapView?
var locationManager = CLLocationManager()
private var infoWindow = MapMarkerWindow()
fileprivate var locationMarker : GMSMarker? = GMSMarker()
override func viewDidLoad() {
super.viewDidLoad()
self.infoWindow = loadNiB()
GMSServices.provideAPIKey("AIzaSyBMppjEPlRBHUD4To2KqNLgmhu1QcxHg3g")
let camera = GMSCameraPosition.camera(withLatitude: 31.516331, longitude: 74.342792, zoom: 6)
mapView = GMSMapView.map(withFrame: .zero, camera: camera)
view = mapView
let states = [
State(name: "Hafeez Center", long: 74.342792, lat: 31.516331, snippets: "Lahore"),
State(name: "Kalma Chowk", long: 74.331553, lat: 31.504532, snippets: "Lahore"),
// the other 51 states here...
]
for state in states {
let state_marker = GMSMarker()
state_marker.position = CLLocationCoordinate2D(latitude: state.lat, longitude: state.long)
state_marker.title = state.name
state_marker.snippet = "Hey, this is \(state.snippets)"
state_marker.map = mapView
}
self.mapView?.isMyLocationEnabled = true
//Location Manager code to fetch current location
self.locationManager.delegate = self
self.locationManager.startUpdatingLocation()
}
func loadNiB() -> MapMarkerWindow {
let infoWindow = MapMarkerWindow.instanceFromNib() as! MapMarkerWindow
return infoWindow
}
//Location Manager delegates
private func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
let camera = GMSCameraPosition.camera(withLatitude: (location?.coordinate.latitude)!, longitude: (location?.coordinate.longitude)!, zoom: 17.0)
self.mapView?.animate(to: camera)
//Finally stop updating location otherwise it will come again and again in this delegate
self.locationManager.stopUpdatingLocation()
}
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
var markerData : NSDictionary?
if let data = marker.userData! as? NSDictionary {
markerData = data
}
locationMarker = marker
infoWindow.removeFromSuperview()
infoWindow = loadNiB()
guard let location = locationMarker?.position else {
print("locationMarker is nil")
return false
}
// Pass the spot data to the info window, and set its delegate to self
infoWindow.spotData = markerData
//infoWindow.delegate = self
// Configure UI properties of info window
infoWindow.alpha = 0.9
infoWindow.layer.cornerRadius = 12
infoWindow.layer.borderWidth = 2
//infoWindow.infoButton.layer.cornerRadius = infoWindow.infoButton.frame.height / 2
let saloonImage = markerData!["image"]!
let name = markerData!["name"]!
let address = markerData!["address"]!
let description = markerData!["description"]!
infoWindow.addressLbl.text = address as? String
infoWindow.nameLbl.text = name as? String
infoWindow.descriptionLbl.text = description as? String
infoWindow.saloonImage.image = saloonImage as? UIImage
//Offset the info window to be directly above the tapped marker
infoWindow.center = mapView.projection.point(for: location)
infoWindow.center.y = infoWindow.center.y - 82
self.view.addSubview(infoWindow)
return false
}
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
if (locationMarker != nil){
guard let location = locationMarker?.position else {
print("locationMarker is nil")
return
}
infoWindow.center = mapView.projection.point(for: location)
infoWindow.center.y = infoWindow.center.y - 82
}
}
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
infoWindow.removeFromSuperview()
}
This is how my pin looks when i tap on it.
From your code it seems that there is a problem in loading Nib.
Please check below code.
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
self.tappedmarker = marker
var point = mapView.projection.point(for: marker.position)
point.y = point.y - 110
let camera = mapView.projection.coordinate(for: point)
let position = GMSCameraUpdate.setTarget(camera)
mapView.animate(with: position)
self.customMarker = CustomMarker.instancefromNib() as! CustomMarker
customMarker.layer.cornerRadius = 10.0
customMarker.clipsToBounds = true
self.customMarker.center = mapView.projection.point(for: marker.position)
self.customMarker.center.y = self.customMarker.center.y - 110
self.view.addSubview(self.customMarker)
self.customMarker.bringSubview(toFront: self.view)
return true
}
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
if (tappedmarker != nil) {
guard let location = tappedmarker?.position else {
print("locationMarker is nil")
return
}
customMarker.center = mapView.projection.point(for: location)
customMarker.center.y = customMarker.center.y - 100
}
}
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
if self.view.subviews .contains(self.customMarker) {
self.customMarker.removeFromSuperview()
return
}
}
Here is the instancefromNib :
class CustomMarker: UIView {
#IBOutlet weak var lblTitle: UILabel!
class func instancefromNib() -> UIView {
return UINib.init(nibName: "CustomMarker", bundle: nil).instantiate(withOwner: self, options: nil).first as! UIView
}
}
This will look like this :
Hope this will help!
Just set your mapView delegate in viewDidLoad()
self.mapView.delegate = self

How to enable Google Maps zoom controls using Swift

I was reading the documentation of Google maps for swift , but compared with Android, I didn't find a way to set the 'Zoom Controls' on my map and by default are disabled.
Exist a way with the Google Maps iOS SDK to display the controls?
I think #Scriptable is right, the documentation hasn't a section for Zoom Controls for iOS SDK.
Well, I made my own (and very basic) controls.
Keep this order (MapView, Button, Button), else, you can't see the buttons.
First one, you must select your UIVIew and change the class to GSMMapView
and, in the MapViewController
import Foundation
import UIKit
import GoogleMaps
class MapViewController: UIViewController {
struct Place {
let id: Int
let name: String
let lat: CLLocationDegrees
let lng: CLLocationDegrees
let icon: String
}
#IBOutlet weak var mapView: GMSMapView!
var markerDict: [Int: GMSMarker] = [:]
var zoom: Float = 15
override func viewDidLoad() {
super.viewDidLoad()
let camera = GMSCameraPosition.camera(withLatitude: 34.1381168, longitude: -118.3555723, zoom: zoom)
self.mapView.camera = camera
do {
if let styleURL = Bundle.main.url(forResource: "style", withExtension: "json") {
mapView.mapStyle = try GMSMapStyle(contentsOfFileURL: styleURL)
} else {
NSLog("Unable to find style.json")
}
} catch {
NSLog("One or more of the map styles failed to load. \(error)")
}
let places = [
Place(id: 0, name: "MrMins", lat: 34.1331168, lng: -118.3550723, icon: "i01"),
]
for place in places {
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: place.lat, longitude: place.lng)
marker.title = place.name
marker.snippet = "Custom snipet message \(place.name)"
marker.appearAnimation = kGMSMarkerAnimationPop
//marker.icon = self.imageWithImage(image: UIImage(named: place.icon)!, scaledToSize: CGSize(width: 35.0, height: 35.0))
marker.map = self.mapView
markerDict[place.id] = marker
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btnZoomIn(_ sender: Any) {
zoom = zoom + 1
self.mapView.animate(toZoom: zoom)
}
#IBAction func btnZoomOut(_ sender: Any) {
zoom = zoom - 1
self.mapView.animate(toZoom: zoom)
}
}
You should catch current value of zoom, because If you hardcode zoom value and user will use not only buttons but buttons and gestures when user will pressed on button after zoom out by gestures you will zoom to old zoom value (very close to map)
to fix this moment you should catch zoom value here (in GMSMapViewDelegate)
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
zoom = mapView.camera.zoom
}
all code will be looks like this
class A: UIViewController {
var zoom: Float = 15
#IBAction func ZoomInButtonPressed(_ sender: UIButton) {
let nextZoom = zoom + 1
mapView.animate(toZoom: nextZoom)
}
}
extension A: GMSMapViewDelegate {
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
zoom = mapView.camera.zoom
}
}

How to identify which GMSMarker was tapped - iOS, Swift

I'm quite new to Swift and i'm trying to better understand Google Maps API. I'm building a simple app that shows images when markers on panoramaView are tapped through didTapMarker method. Since each marker should show a different images, i'm trying to find a way to identify which marker has been tapped, a sort of marker tag.
All suggestions are welcome.
Down here is a prototype of the code with 2 markers and 2 images. Not really sure how to do it, but didTapMarker method should show randomImage when marker is tapped and randomImage2 when marker2 is tapped. So far it only shows randomImage when both marker and marker1 are tapped.
import UIKit
import GoogleMaps
class ViewController: UIViewController, GMSPanoramaViewDelegate {
#IBOutlet weak var viewStreet: UIView!
#IBOutlet weak var randomImage: UIImageView!
#IBOutlet weak var randomImage2: UIImageView!
var panoView: GMSPanoramaView!
override func viewDidLoad() {
super.viewDidLoad()
randomImage.hidden = true
randomImage2.hidden = true
let panoView = GMSPanoramaView(frame: CGRectMake(200, 200, 400, 400))
panoView.delegate = self
panoView.moveNearCoordinate(CLLocationCoordinate2D(latitude: -33.732, longitude: 150.312))
viewStreet.addSubview(panoView)
viewStreet.sendSubviewToBack(panoView)
let position = CLLocationCoordinate2D(latitude: -33.732, longitude: 150.312)
let marker = GMSMarker(position: position)
marker.panoramaView = panoView
let position2 = CLLocationCoordinate2D(latitude: -33.732, longitude: 150.311)
let marker2 = GMSMarker(position: position2)
marker2.panoramaView = panoView
}
func panoramaView(panoramaView: GMSPanoramaView, didTapMarker marker: GMSMarker) -> Bool {
randomImage.hidden = false
randomImage2.hidden = true
return true
}
}
EDIT: solved, thanks to everyone, i'm adding a trivial example on how to do it then.
marker.userData = "example"
Then didTapMarker method is always called when a marker is tapped, but randomImage 's propriety is set to false only when the marker tapped is the one above.
func panoramaView(panoramaView: GMSPanoramaView, didTapMarker marker: GMSMarker) -> Bool {
if marker.userData as? String == "example" {
randomImage.hidden = false
}
return true
}
Code can be improved making use of a dictionary to handle multiple markers but it's up to you. :)
put the data of that marker in userData of that marker. Make use of that that whenever marker is tapped in didTapInfoWindowOfMarker api.

drop a pin when user touch screen in google maps swift

I want to add and remove Marker(pin) in Google Maps.
I want to drop pin with long touch and remove it. I want to use it to select my destination. How can I do it?
let position = CLLocationCoordinate2DMake(10, 10)
let marker = GMSMarker(position: position)
marker.map = mapView
For the ones who're looking for a complete code snippet using Swift:
Implement Protocol GMSMapViewDelegate
Drag an Instance of GMSMapView #IBOutlet weak var googleMapView: GMSMapView!
Mention GMSMapView Delegate within viewDidLoad() as googleMapView.delegate = self
Implement the didTapAt delegate function:
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D){
print("You tapped at \(coordinate.latitude), \(coordinate.longitude)")
googleMapView.clear() // clearing Pin before adding new
let marker = GMSMarker(position: coordinate)
marker.map = googleMapView
}
This code should help you!
//MARK: GMSMapViewDelegate Implimentation.
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
plotMarker(AtCoordinate: coordinate, onMapView: mapView)
}
//MARK: Plot Marker Helper
private func plotMarker(AtCoordinate coordinate : CLLocationCoordinate2D, onMapView vwMap : GMSMapView) {
let marker = GMSMarker(position: coordinate)
marker.map = vwMap
}
PS: Dont forget to confirm to GMSMapViewDelegate in ViewController and assign googleMap.delegate = self somewhere in viewDidLoad()
Hope that helps!

Resources