Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Maps: custom LocationSource in iOS

I have built an Android App which is able to use Google Maps for displaying the users geo location with several additional information beside Wifi, Cells and GPS, for instance QR Codes, Beacons or whatever I could use to signal the current position (I don't take account of the actual accuracy of these locations). I am using the LocationSource and pass it to the Google Maps API via setLocationSource.

I have to use Google Maps as Apple Maps is limited in the available zoom level, so I can't zoom into buildings.

Is it possible to provide a custom LocationManager which gets updated regularly by some action and inject it to Google Maps API like the LocationSource?

I know that there are some hacks which place a custom marker on the map, however this makes it impossible to use the other features of Google Maps like move the camera to the users location by tapping on the "My Location" button.

Thank you.

like image 564
Denis Loh Avatar asked Aug 25 '15 07:08

Denis Loh


People also ask

How do I create a custom map in Google Maps on iPhone?

In the “Your Places” menu that appears on the left, click the “Maps” tab. At the bottom of the menu, select the “Create Map” button. The map creation window will appear in a new tab. To name it, select the “Untitled map” text at the top of the menu on the left.


2 Answers

There is a solution. Google Maps API uses the Cocoa framework class CLLocationManager. Everyone how would like to be informed about current GPS location changes must conform to CLLocationManagerDelegate and be a delegate of the CLLocationManager object. The updates are being sent to the delegate object through the locationManager:didUpdateLocations: delegate method. This is a theory everyone knows about. So in order to simulate location data we need to find a way to invoke this method passing our own data to it. Fortunately there is an opportunity to dynamically change the behaviour of any class, via the functions class_addMethod and class_replaceMethod. So now we can change the implementation of a method abc with a method override_abc which can be implemented in a category like so:

let originalSelector = NSSelectorFromString("setDelegate:")
let newSelector = NSSelectorFromString("override_setDelegate:")

let originalMethod = class_getInstanceMethod(CLLocationManager.self, originalSelector)
let newMethod = class_getInstanceMethod(CLLocationManager.self, newSelector)

method_exchangeImplementations(originalMethod, newMethod)

For our case, we need to change the setter for the location managers delegate and add our own setter as an extension:

extension CLLocationManager {
    func override_setDelegate(delegate: CLLocationManagerDelegate) {
        // save a reference to self
        GMLocationManager = self
        override_setDelegate(delegate)
    }
}

Once we have saved a reference to the Google Maps location manager we can now call locationManager:didUpdateLocations: delegate method using an additional extension method simulateLocation.

extension CLLocationManager {
    func simulateLocation(location: CLLocation) {
        self.delegate?.locationManager?(self, didUpdateLocations: [location])
    }
}
// ...

GMLocationManager?.simulateLocation(fakeLocation)
like image 69
Aleksey Tsyss Avatar answered Sep 29 '22 23:09

Aleksey Tsyss


The simplest solution is that you described. You disable map's "my location" marker and "my location" button, create custom location marker, add custom "my location" button over map, which will go to your custom marker.
For disabling default "my location" elements:

mMap.setMyLocationEnabled(false);
mMap.getUiSettings().setMyLocationButtonEnabled(false);
like image 31
Stas Parshin Avatar answered Sep 30 '22 00:09

Stas Parshin