I am trying to implement a google map inside a UItableviewCell component. The way I am doing this is to define a GMSMapView within the protoype cell, and then using the dequeueReusableCell method i'm configuring the map cell. However, any change i try to apply fails (such as adding markers, camera, zoom, etc.). Does anybody have any information about this issue?
Code reference:
class UITenderInfoMapCell: UITableViewCell {
#IBOutlet weak var view: UIView!
#IBOutlet weak var subView: GMSMapView!
override func awakeFromNib() {
super.awakeFromNib()
self.initMap()
}
/**
Init blank map when initializing a MapCell, waypoints, directions, etc can be loaded later.
**/
func initMap() {
let camera = GMSCameraPosition.camera(withLatitude: 1.285, longitude: 103.848, zoom: 12)
let mapView = GMSMapView.map(withFrame: .zero, camera: camera)
self.subView = mapView
}
Do following steps to add MapView to TableViewCell
Take one UIView in your custom Tableviewcell and give the class name as "GMSmapView" to that UIView
Make sure to connect delegate method of Mapview.
In your ViewController import "GoogleMaps" along with delegate "GMSMapViewDelegate".
Make outlet for MapView like this for your created TableViewCell:
import UIKit
import GoogleMaps
class MapviewTableViewCell: UITableViewCell {
#IBOutlet weak var mapviewObj: GMSMapView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Add Following code in "cellForRowAt"
let cell = tableviewObj.dequeueReusableCell(withIdentifier: "cell") as! MapviewTableViewCell
let marker = GMSMarker()
let lat = Double("13.063754")
let long = Double("80.24358699999993")
marker.position = CLLocationCoordinate2DMake(lat!,long!)
///View for Marker
let DynamicView = UIView(frame: CGRect(x:0, y:0, width:50, height:50))
DynamicView.backgroundColor = UIColor.clear
//Pin image view for Custom Marker
let imageView = UIImageView()
imageView.frame = CGRect(x:0, y:0, width:50, height:35)
imageView.image = UIImage(named:"LocationPin")
//Adding pin image to view for Custom Marker
DynamicView.addSubview(imageView)
UIGraphicsBeginImageContextWithOptions(DynamicView.frame.size, false, UIScreen.main.scale)
DynamicView.layer.render(in: UIGraphicsGetCurrentContext()!)
let imageConverted: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
marker.icon = imageConverted
marker.map = cell.mapviewObj
cell.mapviewObj.camera = GMSCameraPosition.camera(withTarget: marker.position, zoom: 11)
return cell
NOTE:Here I am giving this some extra code to add your custom marker using "DynamicView". If you dont want to add custom marker, you can skip those code. Also Dont forget to configure require settings for google maps from google developer account and add respective key to you appdelegate e.g.
let key = "keep you key here"
GMSServices.provideAPIKey(key)
I added an outlet for GMSMapView as you did, nothing more. I skipped your initMap() method.
And in ViewController cellForRowAt and it works fine for me.
let marker = GMSMarker(position: CLLocationCoordinate2D(latitude: 51.483682, longitude: -0.091991))
marker.map = cell?.subView
What was your issue exactly?
Related
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"
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.
I'm creating a tableViewCell with a mapView inside. however i can't seem to add a annotation based on a location in the viewController containing the tableView. i've started by in the cellForRowAtIndex by setting cell.location = orgObject.coordinate after that i've added following code to my custom cell. However location variable keep returning nil. i guess this is because awakeForNib is called before cellForRowAtIndexPath is called? how do i solve this?
import UIKit
import MapKit
class MapTableViewCell: UITableViewCell, MKMapViewDelegate {
var location: CLLocation?
#IBOutlet var mapView: MKMapView?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
//MapView
mapView!.showsPointsOfInterest = true
if let mapView = self.mapView
{
mapView.delegate = self
}
let orgLocation = CLLocationCoordinate2DMake(location!.coordinate.latitude, location!.coordinate.longitude)
let dropPin = MKPointAnnotation()
dropPin.coordinate = orgLocation
mapView!.addAnnotation(dropPin)
self.mapView?.setRegion(MKCoordinateRegionMakeWithDistance(orgLocation, 500, 500), animated: true)
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialisation code
//MapView
mapView!.showsPointsOfInterest = true
if let mapView = self.mapView
{
mapView.delegate = self
}
}
Call cell.showLocation(location) from cellForRowAtIndexPath
func showLocation(location:CLLocation) {
let orgLocation = CLLocationCoordinate2DMake(location!.coordinate.latitude, location!.coordinate.longitude)
let dropPin = MKPointAnnotation()
dropPin.coordinate = orgLocation
mapView!.addAnnotation(dropPin)
self.mapView?.setRegion(MKCoordinateRegionMakeWithDistance(orgLocation, 500, 500), animated: true)
}
I am new to Xcode and I am trying to show different locations in the MapView. I have been able to set up a MapView, set the coordinates and then link it to a button, that when pressed displays the map with a drop pin.
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var MapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
var latitude:CLLocationDegrees = 54.104456
var longitude: CLLocationDegrees = -6.228926
var latDelta:CLLocationDegrees = 0.001
var longDelta:CLLocationDegrees = 0.001
var theSpan:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)
var clubLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
var theRegion:MKCoordinateRegion = MKCoordinateRegionMake(clubLocation, theSpan)
self.MapView.setRegion(theRegion, animated: true)
var clubAnnotation = MKPointAnnotation()
clubAnnotation.coordinate = clubLocation
clubAnnotation.title = "Football"
self.MapView.addAnnotation(clubAnnotation)
}
}
This is the code I have so far and it works. I want to have multiple buttons that when pressed will show the map with the coordinates set for that location. I want to show only one location for each button on the map.
This might be easy but I am new to code and any help would be great.
You can add buttons from code or from storyboard, here is an example how to add them from code:
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton.buttonWithType(.System) as! UIButton
button.setTitle("football", forState: .Normal)
button.frame = CGRectMake(8, 8, 150, 30)
button.addTarget(self, action: Selector("showFootball:"), forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}
and here is example callback:
func showFootball(sender: UIButton) {
var clubAnnotation = MKPointAnnotation()
clubAnnotation.coordinate = CLLocationCoordinate2DMake(latitude, longitude)
clubAnnotation.title = "Football"
let mw = MKMapView()
mw.removeAnnotations(mw.annotations)
mw.addAnnotation(clubAnnotation)
}
You can add several buttons with different callback using that approach, or you can create an array of locations and assign tags for buttons. Then you can create only one callback and get location based on sender button's tag
I would like keep the MKAnnotaion on the centre of the screen when the user scoll the map like Careem app:
So far I managed to show the pin, update the pin position but when I scroll the map, the annotation in my code initially moves and then gets back to the centre. I would like the pin to remain on the centre.
#IBOutlet var mapView: MKMapView!
var centerAnnotation = MKPointAnnotation()
var manager:CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
manager = CLLocationManager() //instantiate
manager.delegate = self // set the delegate
manager.desiredAccuracy = kCLLocationAccuracyBest // required accurancy
manager.requestWhenInUseAuthorization() // request authorization
manager.startUpdatingLocation() //update location
var lat = manager.location.coordinate.latitude // get lat
var long = manager.location.coordinate.longitude // get long
var coordinate = CLLocationCoordinate2DMake(lat, long)// set coordinate
var latDelta:CLLocationDegrees = 0.01 // set delta
var longDelta:CLLocationDegrees = 0.01 // set long
var span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)
var region:MKCoordinateRegion = MKCoordinateRegionMake(coordinate, span)
self.mapView.setRegion(region, animated: true)
centerAnnotation.coordinate = mapView.centerCoordinate
self.mapView.addAnnotation(centerAnnotation)
}
func mapView(mapView: MKMapView!, regionDidChangeAnimated animated: Bool) {
centerAnnotation.coordinate = mapView.centerCoordinate;
}
****UPDATE****
As suggested by Anna I have added a view to the map. Here the code:
var newPoint = self.mapView.convertCoordinate(mapView.centerCoordinate, toPointToView: self.view)
var pinImage = UIImage(named: "yoga_pins.png")
var imageView = UIImageView(image: pinImage) // set as you want
imageView.image = pinImage
imageView.backgroundColor = UIColor.clearColor()
imageView.contentMode = UIViewContentMode.Center
imageView.center.y = newPoint.y
imageView.center.x = newPoint.x
self.view.addSubview(imageView)
the only problem is that when the map is loaded the first time, the annotation which is located on the mapView.centerCoordinate and I am gonna use to get the latitude and longitude:
when I then scroll the map, the pin moves in the correct position (under the image):
func mapView(mapView: MKMapView!, regionDidChangeAnimated animated: Bool) {
centerAnnotation.coordinate = mapView.centerCoordinate;
}
I recommend you DSCenterPinMapView
It is a custom MapView with an animated and customizable center pin useful for selecting locations in map.
You should install the Pod and then, as a solution to your question you should implement the delegate so that you can get the location where pin drops.
pinMapView.delegate = self
extension MyViewController: DSCenterPinMapViewDelegate {
func didStartDragging() {
// My custom actions
}
func didEndDragging() {
// My custom actions
selectedLocation = pinMapView.mapview.centerCoordinate
}
}
You can use custom button. Add that button on centre of the map. Just show and hide that button according to your conditions.
So when you move map that button stay on same position that is centre of the map.