Issues with Multiple MKMapViews in IOS Application - ios

I've been having an issue with having multiple MKMapViews in my iOS application.
We are currently using a TabBarController for basic navigation. The issue comes up when a MKMapView's annotation's button segues to another view that has a button that leads to another MKMapView. The first MKMapView works fine with annotations and functionality, but the second MKMapView won't add annotations.
I believe the class is linked to the StoryBoard's layout fine since it triggers the viewDidLoad function upon the segue. When I step through the viewDidLoad function it reaches the addAnnotation function call, but the annotations do not get added to the map.
I know the post "Multiple map views in same app" covers a similar issue, but the poster didn't seem too friendly and didn't get any answers due to that.
Please let me know what you think, if you need more information, or if you've implemented multiple MKMapViews in your iOS project. Thanks!

Look closely at your setup of delegates & IBOutlets to make sure each view is pointing to the right mapview. Then make sure each function uses the parameters it was given, e.g.
- (MKAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id < MKAnnotation >)annotation
{
//Only use "aMapView here not "mapView"
}

Related

Mapbox selectAnnotation doesn't work in ViewDidLoad or first time in ViewDidAppear

I have a master-detail application, and my details page is a map. When I click on the element in the Master list, the map with whole bunch of markers shows up, zooms/pans into a specific location, and an annotation pops up describing what this location is.
The implementation is pretty simple, so I thought. In my viewDidAppear, I go through the list of annotations in my mapview and just call [mapView selectAnnotation: myAnnotation animated: FALSE] and it works fine. BUT NOT THE FIRST TIME!
I populate my map in ViewDidLoad, and the first time the ViewDidAppear is called, the mapView.annotations array is still empty. Is there a simple way to make it work even when I first enter the details view? Or do I have to create and populate the map in the AppDelegate?
Try using MGLMapViewDelegate and especially
func mapViewDidFinishLoadingMap(mapView: MGLMapView) {
// your code here
}
Maybe you can define a method at the DetailViewController,then when you trigger the method like tableview's didselect or prepareForSegue,you must get the DetailViewController's instance,finally,you can use this instance to call your user-defined method.

Subclass MKMapView and makes it mapview delegate while still allow other delegate

I have a design problem. Here is what I want to do: I want to constraint MKMapView to a specific region, while making it an abstraction for the view controller which want to actually work with the map.
To constraint the map view I most likely want to use the delegate method mapView:regionDidChangeAnimated: and get notified of the changes and move the map back if the region is out of my pre-determined region. However, since I want to make it generic enough I don't want the code to be in view controller. I thought I might want to sub-class MKMapView instead.
If I do that I would have a subclass of MKMapView (say, a ConstraintMapView class) which is also the delegate of MKMapView and expose the methods to constraint the region to any user of the class. But then the user of the class (say a view controller) would also expect to be a delegate of MKMapView, so I would also want to forward all delegate messages to the view controller.
To do so I need a delegate property which points to the real delegate (the view controller), but in my ConstriantMapView if I have one does that mean I'm overriding the MKMapView's setter and getter to the delegate and things get kind of complicated because inside MKMapView it could call ConstraintMapView's methods and I would give it the view controller but I really want to give it ConstraintMapView instead.
Is there a way to make this work?
Is there a better pattern for the problem that spares the controller from the nitty-gritty of moving the view back to the constrainted region?
I have done a similar proxying MKMapViewDelegate in this project; check it out:
https://github.com/mapbox/mbxmapkit
If you want to over right an Existing class, you can use "The decorator design pattern". Here is the brief explanation. http://www.raywenderlich.com/46988/ios-design-patterns Hope It helps

What is a delegate in iOS? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I've been flashing through some questions here about xcode iOS programming, and I've seen more and more people say something like "as the delegate of blabla" things like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
currentLocationAnnotation = [annotation retain];
}
But I could never help me because I don't understand what this means, and where I have to put it, if I put it in my .m file, it doesn't work, and if I put it in my AppDelegate.m it doesn't work either.
Please help :)
In your code this is the custom delegate what it means it just work like a helper object in which whatever methods had implemented inside custom delegates can be used.
It's a design pattern where you register a object(the delegate), that confirms to a specified protocol, with another instance of some other class that then calls the first objects "protocol methods"(delegate methods) when it wants the delegate to perform some work. This is usually used to avoid unnecessary subclassing when a object just wants to "outsource" part of it's work.
It's a bit hard to get in the beginning. A delegate of a class is like the handler of events of that class. For example, for the map view class, you set a delegate (e.g. your custom class), and in your custom class, start implementing the handlers (it doesn't necessarily need to handle events. For example table view asks its delegate about how many rows it will have, their height etc.). For example, in the example code you've posted, the map view is asking its delegate to return the view for the annotation object that it's sending as a parameter. You need to implement your logic in your class to return the appropriate object.
In English terms, think of it as an email: 'Hello, my delegate, as a part of your job, I'd like you to give me the view for annotation that I'm attaching below, Thanks, Map View.'
Of course, it requires an immediate response from the delegate right now, and the 'attached below' is the 'annotation' parameter. Hope it helps.

didUpdateUserLocation not called when view for userLocation is custom

I have an MKMapView that's supposed to track the user's location using a custom view (not the blue dot). In order to substitute this view for the blue dot, I return it thusly:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if (annotation == [mapView userLocation])
{
return userLocationView;
}
}
To initialize tracking, I call
[mapView setShowsUserLocation: YES];
[mapView setUserTrackingMode: MKUserTrackingModeFollow animated: NO];
[mapView setDelegate: self];
As would be expected, -mapView:didUpdateUserLocation: gets called once when the app loads. Unfortunately, it's never called again unless I change -mapView:viewForAnnotation: to have the following implementation:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if (annotation == [mapView userLocation])
{
return nil;
}
}
With these changes, the map loads the blue dot as the indicator of the user's location, and -mapView:didUpdateUserLocation: gets called frequently, as would be expected.
Is there some sort of mutual exclusivity for tracking users' locations and have a custom user location view? How can I make both happen?
Source
This project demonstrates this issue. https://dl.dropbox.com/u/2338382/MapKitFuckery.zip
Bug
This is most likely a bug, which I've filed as a radar. In the interim, the accepted answer should prove sufficient. However, it bears noting that I had to give up entirely on [mapView userLocation] and [mapView showsUserLocation], in favor of simply a custom annotation and the CLLocationManager.
Instead of relying on the map view's location updates, start a CLLocationManager, set its delegate and wait for -locationManager:didUpdateToLocation:fromLocation: (in iOS 5 and lower) or -locationManager:didUpdateLocations: (iOS 6). You will get much more reliable and plentiful information than using the map view's delegate methods. You probably know the way to do this, but here it is:
#import <CoreLocation/CoreLocation.h>
- (void)viewWillAppear:(BOOL)animated
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager startUpdatingLocation];
}
// Deprecated in iOS 6
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
// Check the age of the newLocation isn't too long ago using newLocation.timestamp
// Set the map dot using newLocation.coordinate
// Set an MKCircle to represent accuracy using newLocation.horizontalAccuracy
}
I had a look at the delegate calls that come in to the mapView's delegate, and returning anything other than nil stops calls to -mapView:didUpdateUserLocation:, like you said. Here are the calls in the order they arrive:
- (void)mapViewWillStartLocatingUser:(MKMapView *)mapView
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation
- (void)mapViewWillStartLoadingMap:(MKMapView *)mapView
- (void)mapView:(MKMapView *)mapView didFailToLocateUserWithError:(NSError *)error
Presumably the MKUserLocation object, not the MKMapView is the object responsible for calling the delegate with update calls. If you check the status of showsUserLocation and mapView.userLocation, they both look fine:
NSLog(#"%d %#", mapView.showsUserLocation, mapView.userLocation);
returns 1 and a non-nil object (1 <MKUserLocation: 0x1e02e580>). Maybe the mapView queries its userLocation object to get the current location, then sends it to the delegate. If that object has gone, it won't work.
It's a bit strange, but like I said, you'll get better updates from a CLLocationManager's updates.
An old question that I would like to answer my own way. I was having an issue somewhat similar. I just needed to make a web service call, passing the user location as a GET parameter, when my MapView's ViewController/Screen was loaded and user location retrieved by the mapView. It made sense then to call the web service within the delegate method didUpdateUserLocation. I didn't notice the wrong behaviour first because things seemed to work properly but then for some reasons sometimes upon opening the mapView screen, the user blue dot would show but the map would not "zoom in" and neither didUpdateUserLocation was called nor my inner web service call obviously. After a few seconds of staring at the screen, the map would "zoom in" and the didUpdateUserLocation/web service was called. Some small glitch I thought, not a big deal. Now my detail-oriented developer's mind was still frustrated and after a few weeks of thinking this over, I decided to take action on this. Stackoverflow didn't give me the answer straight away but pointed me towards the right direction nonetheless. And here was the culprit: Sequence of calls! Maddening but made total sense once I figured things out. I knew that in order to see the blue dot and get the user location I had to tell the mapView to do so. So being an Interface Builder lover, I set things up properly for my mapView in IB, that is I checked the box: "User Location", easy. Then carefully reading the mapView documentation I realised that my ViewController needed to conform to MKMapViewDelegate, done deal. As I said, things seemed to work ok, the blue dot would show right away but sometimes the "zoom in" of the map would take a few seconds... well my iPhone is already 3 years old, things are getting slow, I'd deal with the sluggishness... Then I read this stack overflow post (https://stackoverflow.com/a/37407955) and things became clear. In my case, since using IB, here was the sequence of calls:
User Location checkbox checked in IB
In viewDidLoad, call: mapView.delegate = self
Whereas, here is how the sequence of calls should have been:
User Location checkbox NOT checked in IB (This is important)
mapView.delegate = self
mapView.showsUserLocation = true
And this changes EVERYTHING. Instead of having the mapView zooming in sometimes right away and sometimes after a few seconds, the mapView now zooms in right away when the screen opens, and the didUpdateUserLocation/web service IS called.
Now a little more on the why, it still "sometimes" worked. This is simply due to the fact that the location of my iPhone was sometimes updated right after the map screen was loaded and sometimes not :-). I have to say that besides the stackoverflow post I mentioned above, what helped me too was testing my app in the Simulator since I needed to use a gpx file with obviously static location coordinate, the non predictable behaviour I described was then systematic.
So as much as I love Interface Builder, I might have to reconsider how unconditional that love is.
PS: I know this post is old and my answer not entirely related to the issue #Patrick Perini had but I wish it helps others and that it might answer #Patrick Perini's conclusion regarding the fact that a Bug was at fault when it's not. Thanks for reading this blog post :-)

App Crashes if AddAnnotations doesn't finish

I have an Application, which is a SplitViewController that has a master view on the left and the detail view on the right. One of the views (Branch Finder) is a Map view that loads a series of Annotations to the Map.
If I let the annotations load before switching to any other view (loading the annotations take takes all of 1 second) then everything is fine. However, if the user quickly switches off the Branch Finder view, whilst the annotations are being loaded, then the App will crash with the following notice:
[BranchFinder_iPad respondsToSelector:]: message sent to deallocated instance 0x807d230
Now, my thoughts are that the deallocated instance would refer to the Array (declared in the header of the view) that contains all the annotations being released and set to nil when the user leaves the BranchFinder_iPad view. This is the array that is being passed to the addAnnotations method.
[self.mapView addAnnotations:branchSites];
Has anyone else encountered an issue where leaving a view, mid-way in the add allocations and a crash occurs if the user moves to another view.
Just to clarify:
If I wait for the annotations to load, switching to any other view causes no problem.
I did have a custom annotation view, but I stripped that out of my code (to eliminate it from the mix). Doing this has not changed anything.
I have looked elsewhere for help on this issue, but a lot of the view tutorials regarding map views are single view only, so this issue hasn't arisen.
I have found a vaguely similar issue # the following: mapkit addAnnotations crashes
And finally, I have just made the jump to x-code 4. I think some of my problems are just because I'm relearning some of the things I should know.
Regards,
Nathan A
PS: I wanted to attach an image to this, but am having trouble. I don't have the reputation points to do it natively, and my workplace doesn't allow me access to any image hosting portals. I will endeavour to add an image later today.
Hey anyone who reads this.
I basically performed a rookie mistake here - for the MKMapView in my application, I had to set the delegate to nil as part of the deallocation routine within my view. THe apple documentation makes mention of this in the below document:
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKMapViewDelegate_Protocol/MKMapViewDelegate/MKMapViewDelegate.html
For the relevant section:
Before releasing an MKMapView object for which you have set a delegate, remember to set that object’s delegate property to nil. One place you can do this is in the dealloc method where you dispose of the map view.
Not having this was only causing an issue if I switched to another view AND if the MKMapView was still being referenced in executing code, such as the addAnnotations routine.

Resources