How to get all CFTree Siblings on same depth? - ios

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];

Related

Count number of times object appears in NSArray

I have an NSArray of 5 dice (dice1, dice2, dice3...). Once I have run the random number generator each dice1, dice2, dice3... can return a value between 1-6.
I would like to be able to count how many times a value of 1-6 has been returned.
I'm not too sure of the best way, whether I should turn the int number 1-6 into a string to match.
This is one of those situations where I don't feel that there is a particularly elegant solution due to the inability of Foundation types (such as NSCountedSet) to store intrinsic types (such as int). Swift's automatic boxing/unboxing of ints into NSNumber is a nice feature in this sort of situation.
Since you dealing with a small number of dice and a small number of possible values then you could ignore objects, sets and all that and just loop over your dice array, updating an array of integer counts.
The other, more complex, but object-oriented approach is to create a Die class:
Die.h
#import <Foundation/Foundation.h>
#interface Die : NSObject
-(instancetype)initWithSides:(NSUInteger)sides;
-(instancetype)initWithSides:(NSUInteger)sides initialValue:(NSUInteger)value;
-(NSUInteger)value;
-(NSUInteger)roll;
-(NSUInteger)sides;
#end
Die.m
#import "Die.h"
#interface Die ()
#property NSUInteger currentValue;
#property NSUInteger numberOfsides;
#end
#implementation Die
- (instancetype)initWithSides:(NSUInteger)sides {
NSAssert(sides>1, #"Dice must have at least 2 sides");
if (self = [super init]) {
self.numberOfsides = sides;
[self roll];
}
return self;
}
- (instancetype)initWithSides:(NSUInteger)sides initialValue:(NSUInteger)value {
NSAssert(sides>1, #"Dice must have at least 2 sides");
NSAssert(value <= sides, #"Initial value must not exceed number of sides");
if (self = [super init]) {
self.numberOfsides = sides;
self.currentValue = value;
}
return self;
}
- (NSUInteger)roll {
self.currentValue = arc4random_uniform((UInt32)self.numberOfsides)+1;
return self.currentValue;
}
- (NSUInteger)value {
return self.currentValue;
}
- (NSUInteger)sides {
return self.numberOfsides;
}
- (NSUInteger)hash {
return self.currentValue;
}
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[Die class]]) {
return NO;
}
return [self isEqualToDie:(Die *)object];
}
- (BOOL) isEqualToDie:(Die *)otherDie {
return self.currentValue == otherDie.value;
}
#end
So now you have an object that can be stored in an NSCountedSet and you can retrieve the counts. This bit is slightly awkward since you need to compare to a Die with the appropriate value, not just the value itself:
// self.dice is an array of `Die` objects
NSCountedSet *valueCounts = [NSCountedSet setWithArray:self.dice];
for (int i=1;i<7;i++) {
NSUInteger count = [valueCounts countForObject:[[Die alloc] initWithSides:6 initialValue:i]];
NSLog(#"There are %lu dice showing %d",count,i);
}
use dictionaries :
let arrNum = [“one”, “two”, “three”, “two”]
var countNumber:[String:Int] = [:]
for item in arrNum {
countNumber[item] = (countNumber[item] ?? 0) + 1
}
for (key, value) in countNumber {
print("\(key) occurs \(value) time")
}
o/p :
one occurs 1 time
two occurs 2 time
three occurs 1 time

Mapping a KML file to a Polyline in MKMapView

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.

Logic issue in array indexing

I've probably been starting at this too long and simply can't see the logic problem. I'm changing background images on a pan gesture. Swiping left/right cycles thru an array of image names for the background and loops.
Swiping Right (increasing) works fine, it loops back to the start of my array.
Swiping Left (decreasing stops at the first object in my array (objectIndex: 0).
NSLog(#"_imageBackgroundIndex Before:%d",_imageBackgroundIndex);
if ([_panDirection isEqual:#"Right"])
{
_imageBackgroundIndex = _imageBackgroundIndex + 1;
}
if ([_panDirection isEqual:#"Left"])
{
_imageBackgroundIndex = _imageBackgroundIndex - 1;
}
NSLog(#"_imageBackgroundIndex After:%d",_imageBackgroundIndex);
if (_imageBackgroundIndex > ([_backgroundImages count] - 1))
{
_imageBackgroundIndex = 0;
}
if (_imageBackgroundIndex < 0)
{
_imageBackgroundIndex = ([_backgroundImages count] - 1);
}
[[self childNodeWithName:kBackgroundName] runAction:
[SKAction setTexture:
[SKTexture textureWithImageNamed:
[_backgroundImages objectAtIndex:_imageBackgroundIndex]]]];
Anyone see the issue?
Code looks okay (though there are some style improvements to consider once you get it working). I'd wager that you have _imageBackgroundIndex declared as an unsigned int or NSUInteger. When you think it drops below zero, it's really taking on a huge unsigned value.
This code will work (and has better style) even if the counter is declared unsigned...
- (void)incrementIndex {
BOOL increment = self.imageBackgroundIndex < self.backgroundImages.count-1;
self.imageBackgroundIndex = (increment)? self.imageBackgroundIndex+1 : 0;
}
- (void)decrementIndex {
BOOL decrement = self.imageBackgroundIndex > 0;
self.imageBackgroundIndex = (decrement)? self.imageBackgroundIndex-1 : self.backgroundImages.count-1;
}
Then your pan method becomes simpler:
// not sure how panDirection is initialized, is it really a string #"Left" or #"Right"?
// if so, use isEqualToString to compare strings
if ([_panDirection isEqualToString:#"Right"]) [self incrementIndex];
else if ([_panDirection isEqualToString:#"Left"]) [self decrementIndex];
[[self childNodeWithName:kBackgroundName] runAction:// ... etc

Constructing a NSArray of UIView objects with unique center

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];
}
}

Why am I able to reuse CCActionInterval?

I'm building a simple 2D game in Cocos2d which involves having enemies cross the screen across different, predefined paths. The nextFrame method has the following code:
int indexCount = 0;
for (Enemy *e in enemies) {
if ([cocosGuy doesCollideWithRect: [e boundingBox]])
{
for (Enemy *e in enemies)
{
[self removeChild: e cleanup: YES];
}
[self startGame];
}
// ^ This code not relevant to the question
if ([e numberOfRunningActions] == 0)
{
[e setPosition: [[enemy_positions objectAtIndex:indexCount] CGPointValue]];
[e runAction: [beziers objectAtIndex: indexCount]];
}
++indexCount;
}
The code in the second if statement above is intended to take a CGPoint from the 'enemy_positions' array and a CCActionInterval from the 'beziers' array. It works - when an enemy completes its path, it is repositioned and the action reruns. But why doesn't this break after the action runs for the first time? Aren't CCActions supposed to be one time only?
I ask because I want to refactor the position and action into a single struct, and I want to make sure I know what's going on first. Am I misunderstanding the CCAction class?
Also, here is the current factory method for generating the 'beziers' array:
-(NSArray*) makeBeziers {
ccBezierConfig bezierconf1;
bezierconf1.controlPoint_1 = ccp(-200, 5);
bezierconf1.controlPoint_2 = ccp(300, 100);
bezierconf1.endPosition = ccp(1000,5);
ccBezierConfig bezierconf2;
bezierconf2.controlPoint_1 = ccp(-200, 5);
bezierconf2.controlPoint_2 = ccp(300, 100);
bezierconf2.endPosition = ccp(1000,5);
ccBezierConfig bezierconf3;
bezierconf3.controlPoint_1 = ccp(-200, 5);
bezierconf3.controlPoint_2 = ccp(300, 100);
bezierconf3.endPosition = ccp(1000,5);
NSArray *myarray;
myarray = [[NSArray arrayWithObjects: [CCBezierBy actionWithDuration:3 bezier: bezierconf1],
[CCBezierBy actionWithDuration:3 bezier: bezierconf2],
[CCBezierBy actionWithDuration:3 bezier: bezierconf3],
nil] retain];
return myarray;
}
This works "by accident". Actions are supposed to be one time only. After they've run, they will be released.
However since you store the actions in a separate array, those actions are retained. Therefore you can re-run them. This might work for some actions, other actions may show subtle issues, and some actions may not do anything, leak memory or crash immediately if you do so.
Re-using actions is generally considered bad practice, unless you know the code of each action and you have verified that reusing it doesn't do anything "bad".

Resources