Swift 4/Google Maps.
The crash I get is..
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[UIView setDelegate:]:
unrecognized selector sent to instance 0x7fa1eb50d7e0'
Yes, I know there are a lot of threads on this, but looking through them has not helped me. I have debugged enough where I have a suspect, but do not know exactly how to resolve.
Here's the git repo
Some detail.. The MapViewController has three UIViews.
MapViewController
Main View (default one that comes with the controller)
-- Menu (sub view of Main View)
-- Map (sub view of Main View)
I believe I've narrowed down the crash to the statement
self.mapView.delegate = self
Here's most of the code for the MapViewController.
import UIKit
import GoogleMaps
import FirebaseDatabase
class MapViewController: UIViewController, GMSMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet var mapView: GMSMapView!
var locationOfInterestMarker = GMSMarker()
var userMarker = GMSMarker()
var locationManager = CLLocationManager()
let zoom:Float = 15
let locationOfInterestImage:String = "hhouseicon"
let userMarkerImage:String = "witchicon"
override func viewDidLoad() {
super.viewDidLoad()
self.getLocations()
self.locationManager.delegate = self
self.locationManager.requestAlwaysAuthorization()
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startMonitoringSignificantLocationChanges()
/**
The next line causes crash. I believe I need to set the delegate to something other than self. Set the delegate to the View named Map. However I'm not sure about the actual code for that.
**/
self.mapView.delegate = self //Crashes
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
guard status == .authorizedWhenInUse else {
return
}
self.locationManager.startUpdatingLocation()
self.mapView.isMyLocationEnabled = true
self.mapView.settings.myLocationButton = true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else {
return
}
self.locationManager.stopUpdatingLocation()
self.mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
self.placeMarker(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude, marker: self.userMarker, imageName: userMarkerImage)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if let destination = segue.destination as? LocationDetialViewController {
destination.coordinate = locationManager.location?.coordinate
} else {
return
}
} . . .
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
performSegue(withIdentifier: "locationDetailSegue", sender: self)
return false
}
}//MapViewController
After looking at your code and storyboard.
I can see that you have connected the
#IBOutlet var mapView: GMSMapView!
To the parent view not to actual GMSMapView so please recheck the IBOutlet connections.
I had a similar issue, but the problem was i didn't inherit the module from target
Related
I have a serious problem about use location manage and mapview.
I create a app simple show where the location I am now, it runs ok when only one viewcontroller, but after I added a viewController and segue, and after segue first viewController to map viewController, it has some error message:
location manager (0x101f3d870) was created on a dispatch queue executing on a thread other than the main thread
after that, the App crash with this error message :
MKMapView must be initialized on the main thread
But I really move the code into the main thread, and it does ok before I added first viewcontroller and segue!!! The code at below:
import UIKit
import MapKit
import CoreLocation
class Map_ViewController: UIViewController,MKMapViewDelegate,CLLocationManagerDelegate,UISearchBarDelegate {
#IBOutlet weak var mapView: MKMapView!
var cloaction = CLLocationManager()
var span = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
var currentLat : Double = 0
var currentLon : Double = 0
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
// Start get the location on viewWillAppear
DispatchQueue.main.async {
self.mapView.delegate = self
self.mapView.showsScale = true
self.mapView.showsPointsOfInterest = true
self.mapView.showsUserLocation = true
self.cloaction.requestAlwaysAuthorization()
self.cloaction.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
self.cloaction.delegate = self
self.cloaction.desiredAccuracy = kCLLocationAccuracyBest
self.cloaction.startUpdatingLocation()
}
setCenter()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
mapView.region.center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
mapView.setRegion(mapView.region, animated: false)
}
}
func setCenter() {
let center = CLLocationCoordinate2D(latitude: mapView.userLocation.coordinate.latitude, longitude: mapView.userLocation.coordinate.longitude)
mapView.region.center = center
mapView.region.span = span
mapView.setRegion(mapView.region, animated: true)
}
}
The segue in the main viewController :
override func viewDidLoad() {
super.viewDidLoad()
self.performSegue(withIdentifier: "goToMap", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("goToMapViewController")
}
I have searched the interwebs high and low. I see tons of examples of this same code and the code here is mostly "copy/paste". Yet I can't get it to work.
My ultimate goal is to have interactive elements (UIButton) in the info window of a marker. This marker happens to be the "user marker" or a marker at the spot of the user's location. After some debugging, I can see that some of the code simply never executes, but it is unclear why. I have added comments below to call out the code
My repo is here, feel free to poke at it
class MapViewController: UIViewController, GMSMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet var mapView: GMSMapView!
var customInfoWindow:CustomInfoWindow?
var marker = GMSMarker()
var locationManager = CLLocationManager()
let zoom:Float = 15
override func viewDidLoad() {
super.viewDidLoad()
// ALL THIS WORKS FINE
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
locationManager.startMonitoringSignificantLocationChanges()
self.customInfoWindow = CustomInfoWindow().loadView()
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
guard status == .authorizedWhenInUse else {
return
}
// ALL THIS WORKS FINE
locationManager.startUpdatingLocation()
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else {
return
}
// ALL THIS WORKS FINE
mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: self.zoom, bearing: 0, viewingAngle: 0)
marker.position = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
marker.isTappable = true
//marker.title = "Hi"
marker.map = self.mapView
locationManager.stopUpdatingLocation()
}
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
self.marker = marker
// NONE OF THIS CODE APPEARS TO EXECUTE
//get position of tapped marker
let position = marker.position
mapView.animate(toLocation: position)
let point = mapView.projection.point(for: position)
let newPoint = mapView.projection.coordinate(for: point)
let camera = GMSCameraUpdate.setTarget(newPoint)
mapView.animate(with: camera)
let opaqueWhite = UIColor(white: 1, alpha: 0.85)
customInfoWindow?.layer.backgroundColor = opaqueWhite.cgColor
customInfoWindow?.layer.cornerRadius = 8
customInfoWindow?.center = mapView.projection.point(for: position)
customInfoWindow?.center.y -= 100
customInfoWindow?.customWindowLabel.text = "This is my Custom Info Window"
customInfoWindow?.removeFromSuperview()
self.mapView.addSubview(customInfoWindow!)
return false
}
func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? {
// NONE OF THIS CODE APPEARS TO EXECUTE
return UIView()
}
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
// NONE OF THIS CODE APPEARS TO EXECUTE
customInfoWindow?.removeFromSuperview()
}
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
// NONE OF THIS CODE APPEARS TO EXECUTE - and i don't expect it to unless there is a position change, but wanted to make a note
let position = self.marker.position
customInfoWindow?.center = mapView.projection.point(for: position)
customInfoWindow?.center.y -= 100
}
}
P.S. If you happen to know of a plug and play pod or some other super easy reusable package LMK (Latest Swift version or latest Obj-C would be best)
You can add the delegate to listen to callbacks
self.mapView.delegate = self
The key was to add two lines to the ViewController. First I forgot to instantiate an instance of the custom window view, and then as pointed out above, I forgot to add a delegate for callbacks
self.customInfoWindow = CustomInfoWindow().loadView()
self.mapView.delegate = self
I have integrated GoogleMap using pod in my project but map is not loading. When I make my mapViewController as a initial ViewController then its working fine.
But when I push or present MapViewController then its showing me the blank screen.
I am using XCode 9.1, my project deployment Target is 9.0 and Google Maps SDK for iOS version: 2.5.30219.0.
Thanks in Advance.
Here is my MapViewController code:
import UIKit
import GoogleMaps
class MapViewController: UIViewController {
#IBOutlet weak var mapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
let camera = GMSCameraPosition.camera(withTarget: CLLocationCoordinate2DMake(18.486726, 73.798394), zoom: 10)
mapView.camera = camera
}
}
Console Log:
CoreData: annotation: Failed to load optimized model at path
/Users/pritamsing.salunkhe/Library/Developer/CoreSimulator/Devices/6DD7C0EA-320F-4E99-BEC9-672046198800/data/Containers/Bundle/Application/04CC747F-5B2D-4588-883A-E814D4EE0769/HimmatPlus.app/GoogleMaps.bundle/GMSCacheStorage.momd/StorageWithTileVersionID.omo
Don't use IBOutlet for googleMap in Storyboard & also Don't set subclass for GMSMapView.
implement your map by programmatically. Its will boost your Map UI faster
/// Google MapView
var mapView: GMSMapView!
self.mapView = GMSMapView(frame: CGRect(x: 0, y: 64, width: self.currentDeviceSize.width, height: self.bottomBgView.frame.minY - 64))
// self.mapView.autoresizingMask = [.flexibleBottomMargin, .flexibleTopMargin, .flexibleLeftMargin, .flexibleRightMargin]
self.mapView.settings.allowScrollGesturesDuringRotateOrZoom = true
// self.mapView.settings.myLocationButton = true //Enable My location button
// self.mapView.isTrafficEnabled = true //Enable traffic
// self.mapView.isBuildingsEnabled = true // Building Enable
// self.mapView.isMyLocationEnabled = true //My location Enabled
// let containerView: GMSMapView = self.mapView
self.view.addSubview(self.mapView)
Try with the proper implementation of Google map.
#IBOutlet weak var mapView: GMSMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
self.configureMap()
}
func configureMap(){
mapView.delegate = self
//Check for Location Services
if (CLLocationManager.locationServicesEnabled()) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestWhenInUseAuthorization()
}
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
}
}
MapView Delegate method
//MARK:- MapView delegate
extension YourViewControllerName: GMSMapViewDelegate, CLLocationManagerDelegate{
//Location Manager delegates
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
let camera = GMSCameraPosition.camera(withLatitude: (location?.coordinate.latitude)!, longitude: (location?.coordinate.longitude)!, zoom: 12.0)
self.mapView?.animate(to: camera)
// To stop updating the location again
self.locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error description:\(error.localizedDescription)")
}
}
Key In plist file.
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your custom message.</string>
In a custom tableViewCell , I connect a View which is a google map class to the custom class
However it seems that these line of code doesn't run in the custom cell that I created
This cell is part of a tableView, I tried to instantiate this cell for example var gmapsCell = GMapTableViewCell in the viewDidLoad of the TableView but it seems it will always returns nil
So I change my approach to below codes
import UIKit
import GoogleMaps
class GMapTableViewCell: UITableViewCell, GMSMapViewDelegate {
let locationManager = CLLocationManager()
#IBOutlet var mapView: GMSMapView!
override func awakeFromNib() {
super.awakeFromNib()
locationManager.requestAlwaysAuthorization()
// For use in foreground
locationManager.requestWhenInUseAuthorization()
mapView.delegate = self
mapView.settings.myLocationButton = true
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
extension GMapTableViewCell: CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedWhenInUse {
locationManager.startUpdatingLocation()
mapView.myLocationEnabled = true
mapView.settings.myLocationButton = true
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
locationManager.stopUpdatingLocation()
}
}
}
The problem right now is that
override func awakeFromNib() {
super.awakeFromNib()
locationManager.requestAlwaysAuthorization()
// For use in foreground
locationManager.requestWhenInUseAuthorization()
mapView.delegate = self
mapView.settings.myLocationButton = true
}
This will never run. My goal is simply to get to the device's current location. Are there any equivalent of viewDidLoad for custom cell?
You need to add "NSLocationWhenInUseUsageDescription" key as a string type in info.plist for the location Manager to start updating locations.
All that code inside awakeNib() should be written in tableView:cellForRowAtIndexPath.
Set all the delegates there.
It will work fine then.
I was trying to add a button on top of Google maps. i tried everything i could but no help can anyone help me.. in Xcode preview it shows on top but after running the app it come below google map.
Picture after running app
How it looks in XCODE
CODE OF MAP
import UIKit
import GoogleMaps
class HomeViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var scrollView: UIScrollView!
#IBAction func refreshLocation(sender: AnyObject) {
locationManager.startUpdatingLocation()
}
#IBOutlet weak var gMapView: GMSMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
self.scrollView.contentSize=CGSizeMake(320, 700)
let camera = GMSCameraPosition.cameraWithLatitude(15.4989, longitude: 73.8278, zoom: 17)
gMapView.camera = camera
gMapView.myLocationEnabled = true
self.view.addSubview(gMapView)
let marker = GMSMarker()
marker.map = self.gMapView
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
extension HomeViewController {
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == CLAuthorizationStatus.AuthorizedAlways {
locationManager.startUpdatingLocation()
gMapView.myLocationEnabled = true
gMapView.settings.myLocationButton = true
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
gMapView.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
locationManager.stopUpdatingLocation()
}
}
}
I found the answer
self.view.bringSubviewToFront(self.view_name)
You need to call bringSubviewToFront on the parent view.
Are you using below method? If yes , make sure you are giving frame to mapview so try to give frame as same as you gave in storyboard.
[GMSMapView mapWithFrame:(CGRect) camera:(GMSCameraPosition *)];