I have a array of custom UIView objects with 2 or more objects having same center and I have to construct another array from it with distinct centers. What is the best way to do it?
I tried with below piece of code but it does not work.
self.distinctObjects = [NSMutableArray arrayWithCapacity:iAllObjects.count];
for (MyCustomView *customView in iAllObjects)
{
BOOL hasDuplicate = [[self.distinctObjects filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF.center == %#", customView.center]] count] > 0;
if (!hasDuplicate) {
[self.distinctObjects addObject:customView];
}
}
You can't use struct's (in your case center is a CGPoint) in NSPredicate. Also comparing floating-point values directly isn't a good idea.
You should follow this answer. Just replace [annotation coordinate].latitude with myView.center.x, [annotation coordinate].longitude with myView.center.y, and so on. It should be easy.
BTW your code has O(n^2) complexity, but maybe that's not a problem if the array is small.
I fixed this with below piece of code:
NSMutableArray *uniqueCenters = [NSMutableArray arrayWithCapacity:iAllObjects.count];
self.distinctObjects = [NSMutableArray arrayWithCapacity:iAllObjects.count];
for (MyCustomView *customView in iAllObjects)
{
if (![uniqueCenters containsObject:[NSValue valueWithCGPoint:customView.center]]) {
[uniqueCenters addObject:[NSValue valueWithCGPoint:customView.center]];
[self.beacons addObject:customView];
}
}
Related
Overview:
I have a database of KML files that represent bus routes. I plan to plot a line (sometimes multiple lines if the route is complex) onto a MKMapView in iOS using Objective-C. However, I've been having a terribly difficult time with this, and need your help. You can view a few of the sample KML files I am using here:
Example A: A Full KML file that would be used in production
Example B: A Single Path from the above full file
Example C: A reformatted KML file as JSON
I have two methods of going at this. I have the JSON way and the XML way. Both don't work, with different results problems when using the different example KML files.
In both the JSON and the KML method, I use this code to return the overlay for the mapview
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay{
// Checking if the called overlay is a polyline
if([overlay class] == MKPolyline.class){
// Make the polyline
MKPolyline* polyline = (MKPolyline *)overlay;
#try {
// Extract the title and make sure it isnt nil
NSString *title = polyline.title;
if(title){
// Grab the PolyLine from the container object using the title
MKPolylineRenderer * rtn = [self.polylineContainer objectForKey:title];
if(rtn){
return rtn;
} else {
return nil;
}
}
}
#catch (NSException *exception) {
NSLog(#"%#",exception);
}
} else {
return nil;
}
}
The JSON Way
Personally I'd prefer to use the JSON way because Objective C does a better job with JSON serialization (in my poinion at least) than XML. I have a PHP script set up to just extract the <MultiGeometry> nodes as well as the <LineString> nodes. There doesn't appear to be an issue with this script, so I'll omit it from this question, but if you would like please ask and I'll add it.
The KML way would use example C above and always fails at the [self.mapView addOverlay:polyline]; line with an Unrecognized Selector sent to Instance. It also triggers an EXEC_BAD_ACCESS exception, but I can't trace to where it occurs (even with an exception breakpoint)
// A Synchrnous URLRequest is performed, and the JSON is serialised into response_root
// Metadata. Used for iteration later.
NSDictionary * meta = [response_root valueForKey:#"meta"];
NSInteger mg_count = [[meta valueForKey:#"MultiGeometryCount"] integerValue];
NSInteger ls_count = [[meta valueForKey:#"LineStringCount"] integerValue];
// The Data dictionary holds the data. Obviously
NSDictionary * data = [response_root valueForKey:#"data"];
// mgi is just short for MultiGeometry. It contains LineStrings (lsi)
int mgi = 0;
// Loop through the MultiGeometry nodes
while (mgi < mg_count) {
// Grab the Root Node
NSDictionary * root_node = [data valueForKey:[NSString stringWithFormat:#"root_%d",mgi]];
// lsi is just short for LineString. It contains the coordinates in a JSON object
int lsi = 0;
while (lsi < ls_count) {
// Grab the sub node containing all of the coordinate pairs
NSDictionary * sub_node = [root_node valueForKey:[NSString stringWithFormat:#"node_%d",lsi]];
NSInteger pair_count = [[sub_node valueForKey:#"CoordPairCount"] integerValue];
int pc = 0;
// Set up the C Array for the Coordinates
CLLocationCoordinate2D coordinates[pair_count];
// Loop through the pairs
while (pc < pair_count) {
// Grab the Pair Node
NSDictionary * pair_node = [sub_node valueForKey:[NSString stringWithFormat:#"set_%d",pc]];
// Set X and Y and add them to the coordinate array
double longtitude = [[pair_node valueForKey:#"x"] doubleValue];
double latitude = [[pair_node valueForKey:#"y"] doubleValue];
coordinates[pc].latitude = latitude;
coordinates[pc].longitude = longtitude;
pc ++;
}
// When we've finished with all of the pairs, we create the polyline
MKPolyline * polyline = [[MKPolyline alloc] init];
polyline = [MKPolyline polylineWithCoordinates:coordinates count:pair_count];
if(polyline){
#try {
// This always triggers a "Unrecognised selector sent to instance" exception. Although polyline is correctly set
[self.mapView addOverlay:polyline];
}
#catch (NSException *exception) {
NSLog(#"%#",exception);
}
// Create the rendered line and set its properties
MKPolylineRenderer * line = [[MKPolylineRenderer alloc] initWithPolyline:polyline];
line.strokeColor = [UIColor blueColor];
line.lineWidth = 2;
line.polyline.title = [NSString stringWithFormat:#"ls_%d", lsi];
// Add it to the polyline container, which is just a NSMutableDictionary
[self.polylineContainer setObject:line forKey:[NSString stringWithFormat:#"ls_%d", lsi]];
}
lsi ++;
}
mgi ++;
}
The KML Way
The KML way would use example A and B above and also always fails at the [self.mapView addOverlay:polyline]; line
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"coordinates"]) {
// The coordinate pairs are provided as one big string, I remove the garbage charters from it
coords = [coords stringByReplacingOccurrencesOfString:#"0 -" withString:#"-"];
coords = [coords stringByReplacingOccurrencesOfString:#"\n" withString:#""];
coords = [coords stringByReplacingOccurrencesOfString:#" " withString:#""];
coords = [coords stringByReplacingOccurrencesOfString:#"(null)" withString:#""];
// Split the string into a big array
NSArray * t = [[NSArray alloc] initWithArray:[coords componentsSeparatedByString:#","]];
// Create the C Array for the coordinates
CLLocationCoordinate2D coordinates[t.count];
// Because the X coordinate comes first, I use this toggle to go back between setting the X then the Y and loop
int isXorY = 0;
int i = 0;
double x = 0;
double y = 0;
for (NSString * c in t) {
// X always comes first
if(isXorY == 0){
isXorY = 1;
x = [c doubleValue];
// Then comes the Y
} else if(isXorY == 1){
isXorY = 0;
y = [c doubleValue];
}
// If both the X and the Y coordinate are set, add the pair to the Coordinates array and start over again
if(x != 0 && y != 0){
coordinates[i].latitude = y;
coordinates[i].longitude = x;
x = 0;
y = 0;
i ++;
}
}
// Create the Polyline using the coordinates
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coordinates count:i];
// This always triggers a "Unrecognised selector sent to instance" exception. Although polyline is correctly set
[self.mapView addOverlay:polyline];
// Create the polyline and set its properties
MKPolylineRenderer * line = [[MKPolylineRenderer alloc] initWithPolyline:polyline];
line.strokeColor = [UIColor blueColor];
line.lineWidth = 2;
line.polyline.title = [NSString stringWithFormat:#"ls_%d", totalCordPairs];
// Add it to the container object with its name. Polylinecontainer is just a NSMutableDictionary
[self.polylineContainer setObject:line forKey:[NSString stringWithFormat:#"ls_%d", totalCordPairs]];
// This is global integer that is used with the above name
totalCordPairs ++;
}
}
The results
When this actually does work (really rare) I get completely messed up results. It's easiest just to show you with pictures:
As you can see, the polylines loop back, and sometimes they just go across the map all the way to this village in France! That's where that stray red line is going in the first picture.
There are at least two separate problems causing the behavior you're seeing:
The rendererForOverlay delegate method is getting called before the polylineContainer has been updated with the required MKPolylineRenderer and the delegate method ends up returning nothing (not even nil).
You can't really assume when the map view will call its delegate methods but the rendererForOverlay is called when an overlay is in the visible region. This would also explain why it works "sometimes" which would be when you add the overlay when it's not in the visible region and the delegate method gets called after you've created and added the renderer.
In this case, the polyline's title will still be nil (because you're setting the polyline's title after calling addOverlay).
Since the current code in rendererForOverlay doesn't handle the case where title is nil and the method returns nothing in this scenario.
The method returning nothing (not even nil) is what causes the exception when addOverlay is called. Basically the map view ends up accessing garbage values for the renderer which in your case cause a "unrecognized selector" exception.
It's good practice to always return at least nil at the very end to handle unforeseen cases like this.
The real fix, however, is to move the creation of the renderer to the rendererForOverlay delegate method. You can still keep your polylineContainer approach but if the object is not found in there, then create and add the renderer right then and there in the delegate method.
Here's an example of the fix...
In the section where you create the MKPolyline:
//MKPolyline * polyline = [[MKPolyline alloc] init];
//The above alloc+init is unnecessary since the polylineWithCoordinates
//method effectively does that for you.
MKPolyline * polyline = [MKPolyline polylineWithCoordinates:coordinates
count:pair_count];
//set the polyline's title BEFORE adding it to the map view...
polyline.title = [NSString stringWithFormat:#"ls_%d", lsi];
//Call addOverlay but then do NOT create the renderer
//and add to polylineContainer HERE. Comment that code out.
Then in rendererForOverlay:
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay{
// Checking if the called overlay is a polyline
if([overlay class] == MKPolyline.class){
// Make the polyline
MKPolyline* polyline = (MKPolyline *)overlay;
#try {
// Extract the title and make sure it isnt nil
NSString *title = polyline.title;
if(title){
// Grab the PolyLine from the container object using the title
MKPolylineRenderer * rtn = [self.polylineContainer objectForKey:title];
//HERE, if we did not get already-created renderer,
//create it now and add to polylineContainer...
if (rtn == nil)
{
// Create the rendered line and set its properties
rtn = [[MKPolylineRenderer alloc] initWithPolyline:polyline];
rtn.strokeColor = [UIColor blueColor];
rtn.lineWidth = 2;
// Add it to the polyline container, which is just a NSMutableDictionary
[self.polylineContainer setObject:rtn forKey:title];
}
if(rtn){
return rtn;
} else {
return nil;
}
}
}
#catch (NSException *exception) {
NSLog(#"%#",exception);
}
} else {
return nil;
}
//Always return a default from a method
//that is supposed to return something...
return nil;
}
By the way, it's not clear why you are storing the renderers in polylineContainer. If you're thinking that creating the renderers is expensive and you want to optimize performance, ok but that might be premature at this point (unless you've already found this to be required in your case).
The cause of the lines "looping back" or appearing in unexpected locations like a village in France might be due to just bad data. Look at or log the coordinates you are adding to the polyline and confirm that they are correct. For example, in the JSON file you linked to in the question, there are lots of these:
"set_19":{"y":"0"}
This would end up adding a coordinate at 0,0 which is in the Atlantic Ocean off the west coast of Africa. It seems that either the data or how the code is interpreting the data is wrong.
Is this possible, I searched the net and found no answer to this. My senior also said that this is not possible.
I'm trying to add this as a category, so I want to extract 4 types of objects out of it, all of them uses the same code, it's just the classes that differ so I thought of this:
- (NSDictionary *) getObjectsOfClass:(Class)class
{
NSMutableDictionary *objDict = [NSMutableDictionary dictionary];
[self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:class]) {
/*
Is there a way to do this?
class *label = (class *)obj;
*/
}
}];
return objDict;
}
So is there a way to make this work? It's ugly to see 4 functions with almost the same codes, you agree right?
What about passing class name as string & creating object out of it. May be like this
-(NSArray *)arrayOfObjectsForClass:(NSString *)className{
NSMutableArray *objectArray = [[NSMutableArray alloc]init];
CGFloat yAxis = 10;
for(int i =0; i<5; i++){
id object = [[NSClassFromString(className) alloc]initWithFrame:CGRectMake(0, yAxis, 100, 50)];
[object setTitle:[NSString stringWithFormat:#"Button %d", i+1]];
[objectArray addObject:object];
yAxis+= 60;
}
return objectArray;
}
Because of you said "But I'm adding it to uiview, to get the textfields, labels, pickerviews etc, so that I can just call [self.view getObjectsOfClass:[UILabel class]"
For this code
[self.view getObjectsOfClass:[UILabel class]];
it will return all its UILabel immediate children of it.
- (NSMutableArray *) getObjectsOfClass:(Class)class
{
NSMutableArray *objArray = [NSMutableArray array];
[self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// All visible things are inherited from UIView. Tag property is belongs to UIView
// UILabel are inherited from UIView
if ([self isKindOfClass:[UIView class]] && [obj isKindOfClass:class]) {
UIView *aView = (UIView*)obj;
if (aView.tag == 100) {
//This is the view with tag 100
}
[objArray addObject:obj];
}
}];
return objArray;
}
If all of them are derived from a common base class, you can cast them into that common base class. If few functions are not available then create a category of that common base class and add those common functions into it. This will allow you to have single code block rather than 4 different one.
You can instantiate your class argument like this:
id newInstance = [class new];
What you cannot syntactically do is using class * as a way to tell the compiler which type your local variable is. But this is also not required, thanks to Objective C dynamics typing capabilities.
In other words, there is not reason to cast to class (and you cannot do that; class is only known at runtime, casting has effect at compile time).
EDIT:
If you know a base class common to all of your classes, e.g. UIView, then you could do:
UIView* newInstance = obj;
then access its properties, e.g.:
if (newInstance.tag ==…)
Or you could use message sending instead of properties and do:
if ([obj tag] == ...)
I am working on CFTree. I need to get all siblings on same depth for example in following figure I need an array of all 8 CFTreeRef on 3rd depth. How can I get that?
Following your depth numbering - i.e. starting with root at 1, not 0:
-(void)allNodesFromTree:(CFTreeRef)node currentDepth:(int)currDepth atDepth:(int)depth nodesFound:(NSMutableArray*)nodes
{
bool atTargetDepth = depth -1 == currDepth;
CFTreeRef curChild = CFTreeGetFirstChild(node);
for (; curChild; curChild = CFTreeGetNextSibling(curChild)) {
if(atTargetDepth) //stop recursion if target depth reached
{
[nodes addObject:(__bridge id)(curChild)];
}
else [self allNodesFromTree:curChild currentDepth:currDepth+1 atDepth:depth nodesFound:nodes];
}
}
To make things a bit cleaner, you might wrap it in a helper function:
-(NSMutableArray*)allNodesFromTree:(CFTreeRef)node atDepth:(int)depth
{
NSMutableArray *nodesArray = [[NSMutableArray alloc]init];
[self allNodesFromTree:node currentDepth:0 atDepth:depth nodesFound:nodesArray];
return nodesArray;
}
Then you can just call
NSMutableArray *nodes = [self allNodesFromTree:root atDepth:3];
and thanks for looking...
I'm not even sure how to phrase this question, let alone search for the answer... I have tried, honestly, so all help needed!
It's probably pretty simple as I am sure this is a pattern that happens all the time:
I have an entity in my model (MainLevel*) that has a relationship to itself.
The entity is for levels of a law, and the only requirement is that each law has at least one (the top) level. Beyond that the number of sublevels is, technically, infinite (but in reality about 5 normally and probably no more than 8-10 at most). As might be expected each child level has only one parent (MainLevel.parentLevel) and any parent can have multiple (or zero) children (NSSet *childLevels).
What I would like to do is to get all the structure of this relationship to put in a UITableView or a collectionView.
I have a recursive function as follows:
- (NSDictionary *)getStructureOfLawForLevel:(MainLevel *)level
{
NSMutableDictionary *mutableDict = [[NSMutableDictionary alloc]initWithCapacity:50];
MainLevel *currentLevel = level;
[mutableDict setObject:currentLevel.shortName forKey:#"name"];
if (level.childLevels) {
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"order" ascending:YES];
NSArray *sortdescriptors = #[sortDescriptor];
NSArray *children = [currentLevel.childLevels sortedArrayUsingDescriptors:sortdescriptors];
for (MainLevel *childLevel in children)
{
NSDictionary *dict = [self getStructureOfLawForLevel:childLevel];
[mutableDict setObject:dict forKey:#"sublevels"];
}
}
return [mutableDict copy];
}
Then in viewWillAppear: I have this:
self.structure = [self getStructureOfLawForLevel:self.selectedLevel]
With this I hope I am on the right lines...(untested due to another issue I am sorting right now).
I still cant figure out how to configure a UITableView or a UICollectionView from this though. I mean I am sure I can do it by adding a counter or two and getting the number of lines, and sections, that way. It just seems way, way overcomplicated and I am certain there must be a more obvious method I am just not seeing...
The only criteria for the data is that it must be ordered by the .order attribute of the entity instance, and that is not unique. I mean, for example, each childLevel can have a childLevel with order number 1. It is the order in THAT parent level.
Sorry if this has been asked a thousand times. I have tried to search for an answer but nothing seems to fint the search terms I am using.
I am not doing anything with this data except putting on screen, no editing, adding, deleting... Not sure if that is relevant.
Edit for clarity...
I am not looking to do a drill-down type table view. I want a snapshot of the whole structure in one view, and then I may need to drill down from that (using relationships to other entities in the model).
EDIT FOR MY SOLUTION
Here's what I ended up doing...
- (NSArray *)getStructureAsArrayForLevel:(MainLevel *)child
{
NSMutableArray *thisChildAndChildren = [[NSMutableArray alloc]initWithCapacity:2];
[thisChildAndChildren addObject:child];
if (child.childLevels)
{
// children exist
// sort the children into order
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"order" ascending:YES];
NSArray *sortdescriptors = #[sortDescriptor];
NSArray *children = [child.childLevels sortedArrayUsingDescriptors:sortdescriptors];
// get an array for each child via recursion
for (MainLevel *child in children)
{
[thisChildAndChildren addObject:[self getStructureAsArrayForLevel:child]];
}
}
return [thisChildAndChildren copy];
}
Then I am using similar recursive function to convert the array to NSAttributedString and display in textView.
I really DO NOT like recursion. I don't know why but I find it SOOOOOOO hard to get my head around the logic, and when it's done it seems so obvious... Go figure!
Thanks to everyone for suggestions, help etc...
If you can use a 3rd-party controller, take a look at TLIndexPathTools. It handles tree structures. For example, try running the Outline example project.
Your view controller would look something like this (not much to it):
#import "TLTableViewController.h"
#interface TableViewController : TLTableViewController
#end
#import "TableViewController.h"
#import "TLIndexPathTreeItem.h"
#implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
MainLevel *topLevel = nil;//get the top level object here
TLIndexPathTreeItem *topItem = [self treeItemForLevel:topLevel depth:0];
self.indexPathController.dataModel = [[TLTreeDataModel alloc] initWithTreeItems:#[topItem] collapsedNodeIdentifiers:nil];
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
// customize cell configuration here
TLIndexPathTreeItem *item = [self.dataModel itemAtIndexPath:indexPath];
MainLevel *level = item.data;
cell.textLabel.text = [level description];
}
- (TLIndexPathTreeItem *)treeItemForLevel:(MainLevel *)level depth:(NSInteger)depth
{
NSMutableArray *childItems = [NSMutableArray arrayWithCapacity:50];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"order" ascending:YES];
NSArray *sortdescriptors = #[sortDescriptor];
NSArray *children = [level.childLevels sortedArrayUsingDescriptors:sortdescriptors];
for (MainLevel *child in children) {
TLIndexPathTreeItem *childItem = [self treeItemForLevel:child depth:depth + 1];
[childItems addObject:childItem];
}
//set identifier to some unique identifier, if one exists. Otherwise, the item itself
//will be used as the identifier
id identifier = nil;
//set cell identifier based on depth. This can be set to something unique for each
//depth, or set to a constant value. If nil, the value "Cell" is assumed.
NSString *cellIdentifier = [NSString stringWithFormat:#"Cell%d", depth];
//pass "level" to the data argument. Or pass any other data, e.g. include the depth #[#(depth), level]
TLIndexPathTreeItem *item = [[TLIndexPathTreeItem alloc] initWithIdentifier:identifier sectionName:nil cellIdentifier:cellIdentifier data:level andChildItems:children];
return item;
}
#end
You can subclass TLTreeTableViewController instead of TLTableViewController if you want collapsable levels. Let me know if you need more help.
EDIT
Sorry, I missed the part that says you want to display it all at once. Basically, I think the easiest way to do this would be to basically have a recursive structure that gets the description of each object. This could be a string or even a UIView that you could then place inside your tableviewcell.
Lets stick with a dictionary for now. Each dictionary representation can have information about itself and its children. The template can be:
<LevelInfoDictionary>
<NSObject>someObjectThatRepresentsInfoAboutThisLevel
<NSArray>arrayOfInfoDictionariesThatRepresentChildren
</LevelInfoDictionary>
Then to implement your recursive method:
- (NSDictionary *)getLevelInfo
{
NSMutableArray *childInfo = [NSMutableArray array];
for(ClassName *child in self.children)
{
[childInfo addObject:[child getLevelInfo]];
}
return [NSDictionary dictionaryWithObjectsAndKeys:
<descriptionOfThisLevel>, #"CurrentLevel",
childInfo, #"children>, nil];
}
END EDIT
Basically, as some of these other guys have said, you should create your tableview that displays all of your top level objects. From there, after you select an object, you should be pushed to a new tableview that uses a different fetch request with the predicate like:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"parent = %#",
selectedParentObject];
Then you can use sort descriptors to sort the NSFetchRequest you are using.
Alternatively, you could just fetch the children by using the property on the parent object and store that in an array sorted by your sort descriptors.
One other thing that I should mention is that currently the sort descriptor does not accomplish anything. You may not notice this because there are other parts of the design that you should change, but since an NSDictionary does not have an order (it is a hash table), sorting objects before placing them in a dictionary does nothing.
From the ViewController for the TableView or CollectionView you should start by showing all of the top level objects (No parent). From there as a user selects an object that parent becomes the current level and the ViewController should refresh its data source to show all of the child elements at that level. You can then traverse back up to the parent via back button.
Let me know if you need any more detail.
Your loop doesn't make sense, because you want a list of the dictionaries for the structure of the children, but what you actually do it to overwrite it each time. You probably want something like:
NSMutableArray *subLevels = [NSMutableArray array];
for (MainLevel *childLevel in children)
{
[subLevels addObject:[self getStructureOfLawForLevel:childLevel]];
}
[mutableDict setObject:subLevels forKey:#"sublevels"];
I guess you want to show each level in a table view and drill to another table view for each subsequent level. That should be simple based on the dictionary which gives you a name to display and an optional array which defines whether drilling is possible and the data to pass to the next view controller.
In case it helps someone else, here's what I ended up doing...
- (NSArray *)getStructureAsArrayForLevel:(MainLevel *)child
{
NSMutableArray *thisChildAndChildren = [[NSMutableArray alloc]initWithCapacity:2];
[thisChildAndChildren addObject:child];
if (child.childLevels)
{
// children exist
// sort the children into order
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"order" ascending:YES];
NSArray *sortdescriptors = #[sortDescriptor];
NSArray *children = [child.childLevels sortedArrayUsingDescriptors:sortdescriptors];
// get an array for each child via recursion
for (MainLevel *child in children)
{
[thisChildAndChildren addObject:[self getStructureAsArrayForLevel:child]];
}
}
return [thisChildAndChildren copy];
}
Then I am using similar recursive function to convert the array to NSAttributedString and display in textView.
I really DO NOT like recursion. I don't know why but I find it SOOOOOOO hard to get my head around the logic, and when it's done it seems so obvious... Go figure!
Thanks to everyone for suggestions, help etc...
EDIT
It isn't exactly right, as the first layer has a slightly different structure to those that follow. at some point I need to change this to have the top level as a directory and use the very first level as the key for the directory, then add the complete array as the object for that key. But it works... my brain aches... and I can live with it until I get around to changing it.
This question already has answers here:
NSMutableArray addObject not working
(2 answers)
Closed 9 years ago.
My array:
NSMutableArray *squareLocations;
CGPoint dotOne = CGPointMake(1, 1);
[squareLocations addObject:[NSValue valueWithCGPoint:dotOne]];
CGPoint dotTwo = CGPointMake(10, 10);
[squareLocations addObject:[NSValue valueWithCGPoint:dotTwo]];
CGPoint dotThree = CGPointMake(100, 100);
[squareLocations addObject:[NSValue valueWithCGPoint:dotThree]];
int num = [squareLocations count];
for (int i = 0; i < num; i++)
{
NSValue *pointLocation = [squareLocations objectAtIndex:i];
NSLog(#"%#", pointLocation);
}
When I ask squareLoctions for an object count, it returns zero? But just above asking for count I added three NSValue objects??? Anyone have any ideas?
You need to initialise the array first
NSMutableArray *squareLocations = [[NSMutableArray alloc] init];
That array is returning 0 because you are actually not asking an array for its size.
The object you assume to be an array has neither been allocated, nor being correctly initialised.
You are asking an instance that is currently initialised as being nil. The fun part is that it does not crash as Objective-C will allow you to call any method (selector) on a nil instance (well, that term is not quiet correct). Just, those nil instances will always return 0, NO, 0.0f, 0.0 or nil depending on the expected type when being asked for their return values. In other words, it always returns a value that when being casted towards the expected type, will be set to zero.
To fix that issue you will need to allocate, initialise and assign an instance of an NSMutableArray to your variable.
That could either been done using the proper alloc and init method combination:
NSMutableArray *squareLocations = [[NSMutableArray alloc] init];
Or you could use one of the convenience constructors like for example:
NSMutableArray *squareLocations = [NSMutableArray array];
Next time you encounter such weird behaviour, check if the instance in question is not nil first.