I have a custom annotation that sets its image based on the type of the annotation using the viewForAnnotation delegate method. Im only using 1 annotation that represents a car moving and want to change the image for when the car is detected to be moving and stopped. How could I go about this besides removing my annotation and re-adding it which causes a blink?
Wherever you detect that the car's state has changed, retrieve the annotation's current view using the MKMapView instance method viewForAnnotation:. This is not the same as the mapView:viewForAnnotation: delegate method.
After getting the current view for the annotation, you can modify its properties including image.
Also make sure the mapView:viewForAnnotation: delegate method has the same exact condition to set image based on the state of the car annotation. You may want to put the logic in a common method called from both places (where the state changes and the delegate method) so the code isn't duplicated.
For example, where the state changes, you might have:
//carAnnotation is your id<MKAnnotation> object
MKAnnotationView *av = [mapView viewForAnnotation:carAnnotation];
if (carAnnotation.isMoving)
av.image = [UIImage imageNamed:#"moving.png"];
else
av.image = [UIImage imageNamed:#"stopped.png"];
The if statement (or whatever logic you have to set image) is the part that should also be in the viewForAnnotation delegate method.
Related
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.
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
I have added a button on my .xib file with which I want to delete the last annotation that has been added.
So on the Touch-Down action I have implemented this:
-(IBAction)DeleteAnnotation:(id)sender {
[mapview removeAnnotation:[mapview.annotations lastObject]];
}
and I have even tried it this way:
-(IBAction)DeleteAnnotation:(id)sender {
[self.mapview removeAnnotation:self.mapview.annotations.lastObject]];
}
where mapview is my MKMapView Outlet.
The problem I encounter with both ways is that I have to press this specific button quite a few times before an annotation is removed.
Furthermore, the annotations remove themselves in a quite random way.
Is there something I am doing wrong or is it a software and simulator matter?
The annotations property of MKMapView is not guaranteed to return the annotations in the same order that you added them.
Making the assumption that the annotations array property will return the annotations in the same order that you added them is most likely the reason for the "strange" behavior you see. Please see these related answers for some more details:
MKMapView annotations changing/losing order?
How to reorder MKMapView annotations array
To get the behavior you want (which I assume is simply "remove the last annotation that was added explicitly by my code"), here are three possible approaches (there may be others):
Simplest approach is to keep a reference in a strong property to the last annotation you add (update the reference when you call addAnnotation). When you want to remove the "last annotation added", pass that saved reference to removeAnnotation. For example:
//in the interface...
#property (nonatomic, strong) id<MKAnnotation> lastAnnotationAdded;
//in the implementation...
//when you add an annotation:
[mapview addAnnotation:someAnnotation];
self.lastAnnotationAdded = someAnnotation; //save the reference
//when you want to remove the "last annotation added":
if (self.lastAnnotationAdded != nil)
{
[mapview removeAnnotation:self.lastAnnotationAdded];
self.lastAnnotationAdded = nil;
}
Another option is to loop through the map view's annotations array and search for the "last" annotation (or whatever attribute you're interested in). Once you have a reference to the "last" one (which may not necessarily be the last object in the array), you can call removeAnnotation on it. This approach assumes you have some property in the annotation objects themselves that let you identify an annotation as the "last" one. This may not always be possible.
Another option is to keep your own array of annotations and add annotation objects to this array whenever you call addAnnotation. This is similar to keeping the single reference to just the "last annotation added" except you keep track of the entire list in an order you can rely on. To remove the "last" one, you would get lastObject from your array instead of the map view's (assuming you keep the array in that order). You have to make sure to keep your array in sync as you add/remove annotations from the map.
Simple Question: Seems I cannot find the tag attribute for MKPointAnnotation class. It return error;
MKPointAnnotation *annotation = [[HCIAnnotationViewController alloc]
initwithHouse:house];
The following returns error (Property tag not found ob object of type "MKPointAnnotation"
NSLog(#"%d",annotation.tag);
My question is, if Im not allowed to set the tag, How am I supposed to detect which annotation was clicked.
The other approaches I followed are
Setting tag for MkAnnotationView, However in this what I found out is that the last annotation when added doesn't immediately call for viewForAnnotation (Might be because I'm adding around 1000 MkPointAnnotations to a small map, so it only calls when it comes to view.).
Please tell me how to resolve this?
Since MKPointAnnotation is not a subclass of UIView, has not a property called tag. Bu you have the property coordinate. I assume you have different coordinates for all of your annotations. So you can detect which one.
You just need to compare the coordinates.
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"
}