Searching for particular MKAnnotation class in MKMapView - iphone

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.

Related

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.

Is a MKAnnotation pin connected to the object that holds it? How do I trace back?

I have a NSObject class "MyItem" that holds several instance variables, among them a mapPoint declared in (MyMapPoint *)mapPoint. I use this MKMapPoint to add annotations to a MKMapView. The NSMutablearray "allItems" holds all items.
int all =[allItems count];
int i =0;
for (i=0; i<all; i++) {
MyItem *p = [allItems objectAtIndex:i];
[mYView addAnnotation:[p mapPoint]];
This works perfect. I get a map full of pins where the items were registered. Also, I get a bubble when I push the pin, which gives title and subtitle. I have also managed to add a callout to the console.
NSLog (#"bubble is pushed");
which also works fine.
Problem: When I push the bubble, I want (to begin with) the console to log the full description of the actuall item the pin represents. Is there any sample code that traces back the to full MyItem? It seems like the pin has no recollection of its origin. All help, samplecode and links to samplecode will be heavilly appriciated.
It sounds like you've got a separate type for your annotations (i.e. whatever is returned by the -mapPoint method). An easier way to deal with this is to implement the MKAnnotation protocol directly in your MyItem class. That way, instead of:
[mYView addAnnotation:[p mapPoint]];
you can just say:
[mYView addAnnotation:p];
By adopting MKAnnotation in your data object, you get direct access to the data you need when the user taps an annotation view.
Another way to go, of course, is to stash a pointer back to your data object in you annotation. That can make more sense if your data objects are large, or if you have very many of them. It doesn't even really have to be an actual object pointer -- you just need to store some piece of information in your annotation that lets you later recover the data object. So, for example, you might include an identifier in your annotation. When the user taps the annotation view, you use the identifier to retrieve the associated data from your data store.
Short answer: You're responsible for connecting your annotations to your data; the framework doesn't do it for you.

Sorting objects by the return value of a method

I recently encountered some problems while trying to sort a set of objects.
The objects I'd like to sort are subclasses of NSManagedObject.
I want to sort the objects by a 'global_index', which is, however, not a property in my model. It's just a getter -(NSInteger)globalIndex {...} each of the objects implements. Inside this method, I do some complex calculation which cannot be done with a simple sort descriptor.
Now, my question: Is there a way to make an NSSortDescriptor sort the objects by the return value of a method?
I really want to use a sort descriptor because it's (IMO) the only way to make use of NSFetchedResultsController's cool features. Or is there a way to tell the controller how to sort the fetched objects? Like...
- (NSArray *)sortObjects:(NSSet *)objects {...}
Thanks in advance!
According to the Core Data Programming Guide,
The SQL store, on the other hand, compiles the predicate and sort descriptors to SQL and evaluates the result in the database itself. This is done primarily for performance, but it means that evaluation happens in a non-Cocoa environment, and so sort descriptors (or predicates) that rely on Cocoa cannot work. The supported sort selectors are compare: and caseInsensitiveCompare:, localizedCompare:, localizedCaseInsensitiveCompare:, and localizedStandardCompare: (the latter is Finder-like sorting, and what most people should use most of the time). In addition you cannot sort on transient properties using the SQLite store.
The easy workaround is to save the order with each object (which is a pain when you need to insert an object between two others, but that's a problem with trying to implement an efficient ordered collection in a database).
Have you tried simply passing #"globalIndex" as the sort descriptor's key? That should work fine since there's an accessor for that key.
You should be able to 'trick' NSSortDescriptor into using your global index method like it would any regular accessor method (the accessor should conform to the expectations of Key-Value Programming). In other words, treat globalIndex as a property of your NSManagedObject subclasses and have the following methods in each one of them.
-(NSInteger) globalIndex {
...
}
-(void) setGlobalIndex: (NSInteger) idx {
...
}
To make things even easier, you can define a subclass of NSManagedObject with your extra methods and extend your subclasses from that (or create a Category, either way). Should work just fine.

Core data refactoring

Hi
Normally all the methods like
'- (NSFetchedResultsController *)fetchedResultsController '
are placed in the code of view controllers. I find it a bit messy to write Data Fetching code along with lifecycle methods or table delegate methods.
So my point is should I refactor the CoreData methods to some other helper class say DataLoader and then call them in view controllers?
Is this a wrong thing to do or am I going to loose some coding benefits of Core Data methods.
I would say moving the fetchedResultsController to a helper class is a good idea.
A problem I encounter often is to get the spelling of attributes right.
For example I do a predicate and want to filter on an attribute called #"isSelected". There is no check by the compiler nor by the linker to check the string isSelected. I will have to double check each line where the string has been used.
A search&replace won't work on the misspellings because I don't know what bugs have been introduced.
When I get the predicate wrong then no results will be fetched. Problem is that I don't know if there are no matching rows or if I have filtered wrong. I will need to check at runtime and that consumes time.
For predicates the saved templates exist, so predicates are not a perfect example. But think about value forKey: and we are at square one.
Now if all the fetchedResultsController are in one file then checking would become easier. At least it reduces the possibility of missing that little misspelling in a far away and rarely used class.
...or am I going to loose some coding benefits of Core Data methods.
I tend to say no, but other please feel free to jump in.
#Mann yes of course you can do that without loosing any coding benefits.....if u don't want to write Data Fetching code in you view Controller do not write there.....Make any other class lets say it DataLoader and write the fetching code in method of this class......and call this method by making the object of DataLoader class ....u will be able to fetch data from database
Hope u get it!

Objective C Object Functioning & Passing Arrays

I apologise if this has been asked before but I can't find the info I need.
Basically I want a UITableView to be populated using info from a server, similar to the SeismicXML example. I have the parser as a separate object, is it correct to alloc, init an instance of that parser & then tell RootViewController to make it's table data source a copy of the parser's array.
I can't include code because I haven't written anything yet, I'm just trying to get the design right before I start. Perhaps something like:
xmlParser = [[XMLParser alloc] init];
[xmlParser getXMLData];
// Assuming xmlParser stores results in an array called returnedArray
self.tableDataSource = xmlParser.returnedArray
Is this the best way of doing it?
No, you don't want to do this. You don't want your view controller directly accessing the array of the data-model. This would work in the technical sense but it would be fragile and likely to fail as the project scaled.
As the projects grow in complexity, you will want to increasingly wrap your data model object (in this case the xmlParser) in protective layers of methods to control and verify how the data model changes. Eventually, you will have projects with multiple views, multiple view controllers as well as information entering from both the user and URLs. You need to get into the habit of using the data-model object not just a dumb store you dump stuff into but as an active manager and verifier of your data.
In a situation like this I would have my data-model's array completely wrapped by making it a #protected or #private property. Then I would have dedicated methods for fetching or inserting data into the actual array inside the data-model class itself. No objects outside of the data-model should actually have direct access to the array or have knowledge of its indexes.
So, in this case your data-model would have something like:
- (NSString *) textForLineAtIndexPath:(NSIndexPath *) anIndexPath{
//... do bounds checking for the index
NSString *returnString=[self.privateArray objectAtIndex:anIndexPath.row];
if (returnString=='sometest'){
return returnString;
}
return #""; //return an empty string so the reciever won't nil out and crash
}
as well as a setTextForLineAtPath: method for setting the line if you need that.
The general instructional materials do not spend enough (usually none) time talking about the data-model but the data-model is actually the core of the program. It is where the actual logic of the application resides and therefore it should be one of the most complex and thoroughly tested class in your project.
A good data-model should be interface agnostic i.e. it should work with a view based interface, a web based interface or even the command line. It should neither know nor care that its data will be displayed in a tableview or any other interface element or type.
When I start a new project, the first thing I do is comment out the '[window makeKeyAndVisible];' in the app delegate. Then I create my data-model class and test it old-school by loading data and logging the outputs. Only when it works exactly how I wish it to do I then proceed to the user interface.
So, think real hard about what you want the app to do on an abstract level. Encode that logic in a custom class. Isolate the data from all direct manipulation from any other object. Verify all inputs to the data before committing.
It sounds like a lot of work and it is. It feels like overkill for a small project and in many cases it is. However, getting the habit early will pay big dividends very quickly as your apps grow in complexity.
Not quite. You want the data source to be an object that implements the UITableViewDataSource protocol; what I would do in this situation is create an object that implements that protocol and parses XML, so that you can alloc-init it, then set the data source to that object and have it update the table view as appropriate. So based off your code (and assuming you're running within the table view's controller):
XMLParserAndDataSource xpads = [[XMLParserAndDataSource alloc] init];
[xpads getXMLData];
self.tableView.dataSource = xpads;
It's probably a good idea to give this class itself a reference to an NSXMLParser object, so you can use that to parse the XML, then provide convenience methods (like getXMLData) as well as the UITableViewDataSource methods for your own use. (If you go this route, you should also make your XMLParserAndDataSource class implement the more useful of the NSXMLParser delegate methods, and use them as appropriate to update your table view.)
I'm a Mac programmer and not an iPhone programmer; but on the mac,
self.tableDataSource = xmlParser.returnedArray is not correct. You are supposed to either bind the table's content to an Array Controller (if iPhone has one?) or set the datasource outlet to your RootViewController.
In your rootview controller, you would implement the methods:
– tableView:cellForRowAtIndexPath:
– tableView:numberOfRowsInSection:
For – tableView:cellForRowAtIndexPath: you would return a UITableViewCell with the data you received from the XML parsing according to the index path like so:
UITableCell *myCell = [UITableCell new];
myCell.textLabel.text = [parsedXMLArray objectAtIndex:[indexPath indexAtPosition:indexPath.length-1]];
return myCell;
(Something people don't know is that you can use the + new class method on all NSObject subclasses which automatically call alloc/init.)
For – tableView:numberOfRowsInSection just return the count of the data array:
return parsedXMLArray.count;
Can't edit my question nor post replies, can only post my response as answer.
#TechZen: I'm somebody who tries to form analogies, helps me understand. What you're saying is something like: My original idea was like going into the file room & dumping all the originals on my desk to work on where as you suggest the object be more like an organised file clerk who will search through the data for me and only return the specific datum that I need while being the only one with direct access to that data.
Have I understood correctly?
#Tim: What if I later need the parser to get data for something which is not a table? That's why I thought to dump it into an array & let the caller decide what to do with the data. Would you suggest a second object that would supply the data in the newly required form? (Am I sort of one the right track here or way off?)