Updating MKMapView Annotations from Updated array? - iphone

I am working with an NSMutableArray of objects that conform to the MKAnnotation protocol. My question is over time new objects are added to the array, can anyone tell me what is the preferred method for updating the annotations on the mapView. Should I be looking at removing all the pins before adding back the updated array, or would I be better to mark/tag existing pins in the MKAnnotation object and only add back the new (un-tagged) pins?

Removing all the pins and adding back the whole array including the new annotations will result in flicker and unnecessarily redrawing pins that haven't changed.
Unless the flicker is desired or a full refresh is necessary for some reason, it's better to just tell the map view to add the new pins.
After your main annotation array is updated with the new pins, construct a temporary array called say newAnnots containing references to the new annotations in the main array and pass newAnnots to the map view's addAnnotations: method. The temporary array can be discarded afterwards.
However, instead of using tagging to identify "new" annotations, you could just check if the annotation object in your main array already exists in the map view's annotations array. For example:
if (![mapView.annotations containsObject:annot_from_your_main_array]) {
[newAnnots addObject:annot_from_your_main_array];
}
Comparing with the map view's annotations array will only work if the annotation objects in your main array are the actual annotations you give to the map view in addAnnotation: or addAnnotations:. Also, when your main array is "updated", it should only add the new annotations instead of rebuilding the whole array from scratch. If it does, the annotation references won't match with the ones in the map view's array.
The same applies if you are removing annotations on an update. The removed annotations could be added to a temporary "remove" list (by checking if annotations in the map view's array exist in your array) and passed to removeAnnotations:.
Note that if you update an existing annotation's coordinates in your main array, the map view will automatically update the pin's location as long as the annotation object in your array implements the setCoordinate: method.

Related

How BehaviorRelay.accept works in rxswift

I am drawing a tableview via BehaviorRelay.
Currently, I am using the code below as a way to add data.
viewModel.user.append(Person(name: "king", phoneNumber: "12341234"))
viewModel.personObservable.accept(viewModel.user)
I wonder if this code changes the user itself so that the whole tableView is redrawn.
If so, what method can I use to change only the data I added?
The code presented causes the personObservable (which is actually a BehaviorRelay apparently,) to emit a next event that contains an entire array of Person values, not just the latest Person added. Importantly, it's not emitting the viewModel.user object (at least not conceptually) but an entirely different object that happens to be equal to viewModel.user.
The default dataSource, the one that you get when you call items with anything other than a DataSource object, will call reloadData on the table view. This doesn't cause "the whole tableView" to be redrawn though, but it will cause the table view to query the data source for all of the visible cells, even if they haven't changed.
If you only want the table view to load the new cell, then the data source object needs to be smart enough to compare the new array with the array it's currently displaying so it can figure out which values are different and add/remove/move cells as appropriate, instead of just calling reloadData. As #Sweeper said in the comments, the RxDataSources library contains a set of data source classes that have that logic built in. If you wanted to reinvent the wheel, just write a class that conforms to both RxTableViewDataSourceType & UITableViewDataSource and implement the diffing yourself.

How can I know if an annotation is already on the mapview

I trying to add annotation pin to Mapview if and only if there is no such same pin already on mapview to avoid having multiple repeat annotations at the same location.
Any one can show me help?
You can use mapView.view(for:) method.
e.g.
if (self.mapView.view(for: annotation) != nil) {
print("pin already on mapview")
}
The approach suggested by #Kosuke Ogawa would work. However it is not a good idea to rely on the UI determine application state.
For eg: You most probably have a list of annotations stored in some sort of data structure in your view controller (I am guessing an Array). This data structure should be the source of truth.
In your case, to determine if a an Annotation is already on the map, check if the Data structure that feeds the mapview contains the annotation and proceed.

How to remove country/state names from MKMapview

I want to remove country / state names from MKMapview in iPhone. Means I need only blank map view without any name on it. Is this possible with MKMapview or any other third party maps.
I want to use this functionality with MKMapTypeStandard map type.
Have a look at MKMapViews property mapType MKMapTypeSatellite. Setting this type you will see a map without any names on it. See the docs:
MkMapView Class Reference
Code:
self.map.mapType = MKMapTypeSatellite;

Searching for particular MKAnnotation class in MKMapView

I have 4-5 kinds of different annotations classes in mapView.
With following code I expect only AnnotationType1 should respond to for loop.
for (AnnotationType1* annotation in mymap.annotations)
{
NSLog(#"annotation class is %#", [annotation class]);
}
But as is evident from console I get other classes also.
annotation class is AnnotationType1
annotation class is AnnotationType2
annotation class is AnnotationType3
annotation class is AnnotationType4
what will be the best way to perform actions only on say AnnotationType1 annotation?
First, as you've discovered, fast iteration doesn't work the way you thought it did. mymap.annotations returns the same array of annotation objects no matter what -- it doesn't have any idea what kind of pointer you're assigning them to.
Second, it's usually considered a bad idea to count on a view (such as MKMapView) to store data (like your annotations). It's fine for the map view to know about the annotations -- it must know about them to do its job properly. But I wouldn't recommend counting on the map view to maintain the app's state. You probably have the annotation objects stored somewhere in your data model -- if so, that'd be a better place to get the list of annotations.
Third, you can filter the array using a predicate. See this answer for help using a predicate to filter by class name.

The "coordinate" property of MKAnnotation Protocol

My question is about the "readonly" attribute of the "coordinate" property. In the Protocol there is also a "setCoordinate" instant method listed. It says in the documentation that it is meant to support "dragging".
My question is :
1
If the coordinate can be set, then why there is a "readonly" attribute assigned to it ?
2
I am thinking of using a single temporary MKAnnotation object to populate an array. The scheme is to assign different coordinate values and add it to the array repeatedly. But the scheme would not work if the coordinate property is "readonly". Or can I use the "setCoordinate" for this purpose anyway ?
Am just wish to avoid having to create multiple MKAnnotation objects to populate the array (since the array can potentially be more that just a few points).
Hope that somebody knowledgable in this area could help ...
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKAnnotation_Protocol/Reference/Reference.html
The setCoordinate: method is optional. If your annotation supports dragging, you can implement it but you don't have to. The readonly property on the other hand is mandatory.
I am thinking of using a single temporary MKAnnotation object to populate an array. The scheme is to assign different coordinate values and add it to the array repeatedly. But the scheme would not work if the coordinate property is "readonly". Or can I use the "setCoordinate" for this purpose anyway ?
This scheme won't work in any case. If you add the annotation to your array, then modify the coordinate and add it again, you'll have the exact same annotation twice in your array, with the same coordinates. If you want an array of annotations with different coordinates, you will need to create a distinct annotation object for each coordinate.