Getting leaks in profiler when google map loads. I have created a very simple View Controller based on google's example code and Im finding I am getting a leak when the map loads. I believe the leak is in the SDK itself. Has anyone come across this problem and found a solution?
basic View Controller
//
// JRCViewController.m
// GoogleMapsInterface
//
// Created by Jake Cunningham on 15/01/2014.
// Copyright (c) 2014 Jake Cunningham. All rights reserved.
//
#import "JRCViewController.h"
#interface JRCViewController (){
BOOL firstLocationUpdate_;
GMSMapView *mapView;
}
#end
#implementation JRCViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.868
longitude:151.2086
zoom:6];
mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera];
[mapView addObserver:self
forKeyPath:#"myLocation"
options:NSKeyValueObservingOptionNew
context:NULL];
self.view = mapView;
dispatch_async(dispatch_get_main_queue(), ^{
mapView.myLocationEnabled = YES;
});
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if (!firstLocationUpdate_) {
// If the first location update has not yet been recieved, then jump to that
// location.
firstLocationUpdate_ = YES;
CLLocation *location = [change objectForKey:NSKeyValueChangeNewKey];
mapView.camera = [GMSCameraPosition cameraWithTarget:location.coordinate
zoom:14];
}
}
#end
I found the reason for the problem. Like you, I thought that their "demo code" was supposed to work as is. Not understanding that this line
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.868
longitude:151.2086
zoom:6];
The very first line in the demo was actually the problem.
If you are not in Australia (which is where this location point is)
You result in loading the entire world map (tiles) into the memory of the app, which is wrong!
If you know approximately which continent/country/state your map will be used
or better yet! get the user's location BEFORE you show the map, and load the map at that location.
So your initialization of the map should be this:
CLLocation *location = [self getUserLocation]; //probably from shared prefs, even if its 100 miles away from where the user actually is, is better than loading another continent.
and then your viewDidLoad would look like this
- (void)viewDidLoad
{
[super viewDidLoad];
CLLocation *location = [self getUserLocation]; //<== very important!
// Do any additional setup after loading the view, typically from a nib.
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:location.coordinate.latitude
longitude:location.coordinate.longitude
zoom:15];
mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera];
[mapView addObserver:self
forKeyPath:#"myLocation"
options:NSKeyValueObservingOptionNew
context:NULL];
self.view = mapView;
dispatch_async(dispatch_get_main_queue(), ^{
mapView.myLocationEnabled = YES;
});
}
The zoom level also has an effect on this -> the bigger the zoom is, the less tiles you load into the memory.
also I added code in viewWillDisappear (assuming that viewDidLoad will run again when the given ViewController will be needed again)
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[self.mapView clear];
[self.mapView removeFromSuperview] ;
self.mapView.delegate = nil;
self.mapView = nil ;
}
This helped my app from having to use 140 MB of ram to just 56!
When app normal is between 40 and 45.
Related
I am using Google Maps SDK to get the current location. My doubt is, how to do location updates using the delegate methods using Google Maps SDK. Is there any delegate methods for getting the current location and location updates?
#implementation MyLocationViewController {
GMSMapView *mapView_;
}
- (void)viewDidLoad {
[super viewDidLoad];
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.868 longitude:151.2086 zoom:12];
mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera];
mapView_.settings.compassButton = YES;
mapView_.settings.myLocationButton = YES;
// Listen to the myLocation property of GMSMapView.
[mapView_ addObserver:self forKeyPath:#"myLocation"
options:NSKeyValueObservingOptionNew context:NULL];
self.view = mapView_;
// Ask for My Location data after the map has already been added to the UI.
dispatch_async(dispatch_get_main_queue(), ^{
mapView_.myLocationEnabled = YES;
});
}
In order to get current location on iOS , refer to following link:
How can I get current location from user in iOS
You can use CLLocationManager delegate methods to update location and set the location to the map.
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;
This delegate method will update the location. Inside the method, you can set the map view to the coordinates received.
_currentLocation = [locations lastObject];
CLLocationCoordinate2D target =
CLLocationCoordinate2DMake(_currentLocation.coordinate.latitude, _currentLocation(This is the current location of the user).coordinate.longitude);
_googleMap.camera = [GMSCameraPosition cameraWithTarget:target zoom:16];
I have a DetailViewController that is to be shown when user taps a marker on the mapView_. Initially I load the VC and set it hidden, and when the user would tap a marker, I would load values in the VC and show it. But it is not happening. Here's my code.
#property (strong, nonatomic) GMSMapView *mapView;
- (void)viewDidLoad
{
[super viewDidLoad];
_whisperDetailView.hidden = YES;
[_whisperDetailView initlize];
_whisperDetailView.viewController = self;
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.86
longitude:151.20
zoom:6];
mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera];
mapView_.myLocationEnabled = YES;
self.view = mapView_;
}
- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker
{
_detailView.whisper = marker.userData;
_detailView.hidden = NO;
return YES;
}
Storyboard snapshot:
Funny thing is I was using Mapbox before and was doing exact same thing in its didSelectAnnotation function and everything was working great. I recently had to switch to Google Map and now its not working.
Any help is appreciated. Thanks.
Okay I've got it working now! Posting the answer for anyone else stuck in same situation:
I changed the way I was adding my mapView_. instead of assigning self.view = mapView in viewDidLoad, I inserted mapView as a subview to my main view, like this:
mapView_ = [GMSMapView mapWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height) camera:camera];
[self.view insertSubview:mapView_ atIndex:0];
And alas! everything works like a charm now! When I tap a marker, my DetailViewController shows with proper sizes and as I set it up in storyboard.
I dropped the maps from iOS and switched to google maps. Now it shows my position but it doesn't load the map. It is gray / yellow with in the bottom left Google.
This is my implementation:
#import <CoreLocation/CoreLocation.h>
#interface GoogleMapViewController ()
#property (strong, nonatomic) CLLocationManager *locationManager;
#end
#implementation GoogleMapViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Create a GMSCameraPosition that tells the map to display the
// coordinate -33.86,151.20 at zoom level 6.
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.86
longitude:151.20
zoom:6];
_mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera];
_mapView.myLocationEnabled = YES;
self.view = _mapView;
[self initLocationManager];
// Creates a marker in the center of the map.
GMSMarker *marker = [[GMSMarker alloc] init];
marker.position = CLLocationCoordinate2DMake(-33.86, 151.20);
marker.title = #"Sydney";
marker.snippet = #"Australia";
marker.map = _mapView;
}
- (void) initLocationManager
{
self.locationManager = [[CLLocationManager alloc] init] ;
self.locationManager.delegate = (id)self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation ;
[self.locationManager setDistanceFilter:10.0f] ;
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
NSLog(#"New locations");
[self.locationManager stopUpdatingLocation];
_mapView.camera = [GMSCameraPosition cameraWithTarget:newLocation.coordinate
zoom:6];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
This is basically the tutorial demo app from Google Dev page, and I'm not sure why it's not loading the map.
This is what I get:
I found the problem. You need to set the initial camera zoom level on 1 and then zoom in on your current location. That way you can still zoom out and have the whole map available.
If you take a zoomlevel 14 as initial, google maps doesn't load the rest of the map I think.
I have created an iPhone Application running version 6.1.
In one of my views i am using a mapview including annotation pin, zoom to location.
The app runs fine if all of the other views has been opened first and the mapview dont crash.
However, if i open the app and go straigt to the mapview viewcontroller the app crashes??
Furthermore the app only crashes on my iPhone 4S device, and NOT in the controler???
Can anyone help me please??
Best regards.
Here is my code for the mapview:
#interface VisPaaKortViewController ()
#end
#implementation VisPaaKortViewController
#synthesize mapView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.mapView.delegate = self;
mapView.showsUserLocation = YES;
// Do any additional setup after loading the view.
// Create a view of the standard size at the top of the screen.
// Available AdSize constants are explained in GADAdSize.h.
bannerView_ = [[GADBannerView alloc] initWithAdSize:kGADAdSizeBanner];
// Specify the ad's "unit identifier". This is your AdMob Publisher ID.
bannerView_.adUnitID = #"my_publisher_id";
// Let the runtime know which UIViewController to restore after taking
// the user wherever the ad goes and add it to the view hierarchy.
bannerView_.rootViewController = self;
[self.view addSubview:bannerView_];
//Placement of banner
[bannerView_ setFrame:CGRectMake(0,
362,
bannerView_.bounds.size.width,
bannerView_.bounds.size.height)];
// Initiate a generic request to load it with an ad.
[bannerView_ loadRequest:[GADRequest request]];
}
-(void) viewWillAppear:(BOOL)animated {
[self performSelector:#selector(zoomInToMyLocation)
withObject:nil
afterDelay:0];
}
- (void)zoomInToMyLocation
{
CLLocationCoordinate2D location;
location.latitude = (double) 59.778747;
location.longitude = (double) 9.292349;
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(location, 800, 800);
[self.mapView setRegion:[self.mapView regionThatFits:region] animated:NO];
// Add an annotation
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = location;
point.title = #"Text";
point.subtitle = #"Text";
[self.mapView addAnnotation:point];
[self.mapView selectAnnotation:[[self.mapView annotations] objectAtIndex:0] animated:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
Since you don't provide much detail I have to guess. I assume that you use ARC, and I would try to add self.mapView.delegate = nil; to your dealloc method.
- (void)dealloc {
self.mapView.delegate = nil;
}
MKMapViews delegate is declared assign and not weak, which means it's possible that the delegate can still be referenced when the viewController is already deallocated.
And if you don't use ARC you should still nil the mapView.delegate in your dealloc.
I am currently working with Google Maps API in my project. I am trying to set the default camera/zoom to the users location. I do this:
#implementation ViewController{
GMSMapView *mapView_;
}
#synthesize currentLatitude,currentLongitude;
- (void)viewDidLoad
{
[super viewDidLoad];
mapView_.settings.myLocationButton = YES;
mapView_.myLocationEnabled = YES;
}
- (void)loadView{
CLLocation *myLocation = mapView_.myLocation;
GMSMarker *marker = [[GMSMarker alloc] init];
marker.position = CLLocationCoordinate2DMake(myLocation.coordinate.latitude, myLocation.coordinate.longitude);
marker.title = #"Current Location";
marker.map = mapView_;
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:myLocation.coordinate.latitude
longitude:myLocation.coordinate.longitude
zoom:6];
mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera];
self.view = mapView_;
NSLog(#"%f, %f", myLocation.coordinate.latitude, myLocation.coordinate.longitude);
}
However, it does not work, since when I do
NSLog(#"%f, %f", myLocation.coordinate.latitude, myLocation.coordinate.longitude);
it returns 0, 0, and it does not give the current location coordinates. How can I properly get the user's coordinates?
.h
#import <CoreLocation/CoreLocation.h>
#property(nonatomic,retain) CLLocationManager *locationManager;
.m
- (NSString *)deviceLocation
{
NSString *theLocation = [NSString stringWithFormat:#"latitude: %f longitude: %f", locationManager.location.coordinate.latitude, locationManager.location.coordinate.longitude];
return theLocation;
}
- (void)viewDidLoad
{
locationManager = [[CLLocationManager alloc] init];
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
[locationManager startUpdatingLocation];
}
answered here.
When an app first starts it may not yet know your location, as it usually takes a while for the GPS device to lock on to your location (if it has just been started), and especially if this is the first time the application has been run, and so the user hasn't yet answered the prompt to give the app access to their location. Also it seems like mapView.myLocation is always empty (either nil or has coordinates 0,0) when the map view has just been created.
So you will need to wait until the user's location is known, and then update the camera position.
One way might be using the code at how to get current location in google map sdk in iphone as suggested by Puneet, but note that the sample code there is missing the details of setting up the location manager (like setting the location manager's delegate), which might be why it didn't work for you.
Another option could be to use KVO on mapView.myLocation, as described here: about positioning myself,some problems
By the way in your sample code you are accessing mapView.myLocation before you create the mapView, and so the location would always be nil anyway.
I just Downloaded the new GoogleMap SDK Demo for iOS. Here is what I have seen from the source code that how the "Current Location" is achieved via KVO.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "SDKDemos/Samples/MyLocationViewController.h"
#import <GoogleMaps/GoogleMaps.h>
#implementation MyLocationViewController {
GMSMapView *mapView_;
BOOL firstLocationUpdate_;
}
- (void)viewDidLoad {
[super viewDidLoad];
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.868
longitude:151.2086
zoom:12];
mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera];
mapView_.settings.compassButton = YES;
mapView_.settings.myLocationButton = YES;
// Listen to the myLocation property of GMSMapView.
[mapView_ addObserver:self
forKeyPath:#"myLocation"
options:NSKeyValueObservingOptionNew
context:NULL];
self.view = mapView_;
// Ask for My Location data after the map has already been added to the UI.
dispatch_async(dispatch_get_main_queue(), ^{
mapView_.myLocationEnabled = YES;
});
}
- (void)dealloc {
[mapView_ removeObserver:self
forKeyPath:#"myLocation"
context:NULL];
}
#pragma mark - KVO updates
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if (!firstLocationUpdate_) {
// If the first location update has not yet been recieved, then jump to that
// location.
firstLocationUpdate_ = YES;
CLLocation *location = [change objectForKey:NSKeyValueChangeNewKey];
mapView_.camera = [GMSCameraPosition cameraWithTarget:location.coordinate
zoom:14];
}
}
#end
Hope it can help you.
Just in case anyone wants DrDev's excellent answer in Swift 3:
var locationManager: CLLocationManager?
func deviceLocation() -> String {
let theLocation: String = "latitude: \(locationManager.location.coordinate.latitude) longitude: \(locationManager.location.coordinate.longitude)"
return theLocation
}
func viewDidLoad() {
locationManager = CLLocationManager()
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
// 100 m
locationManager.startUpdatingLocation()
}