Cocos2d Remove a ChildByName - objective-c

Hi I'm trying to add multiple nodes that are the same and assign them different names and then remove one of them when the count gets above 2. All of this works except it is crashing when removing the child by name and returning the error "Invalid Name" can anyone help? I think it is a little different because I am using Sprite Builder and also cocos2d removed "removeChildrenByTag"
-(void)addTowers:(NSTimer *) timer
{
towerCount++;
if (_towerDown.children.count >= 2) {
[_towerUp removeChildByName:#"tower1" cleanup:YES];
towerCount = 0;
}
else
{
//Tower Up
towerUp = [CCBReader load:#"TowerUp"];
towerUp.name = [NSString stringWithFormat:#"tower%d",towerCount];
[_towerUp addChild:towerUp z:1 name:towerUp.name];
}

There's a silly bug in the Cocos2d v3 code. Upgrade to the latest version or change
NSAssert( !name, #"Invalid name");
to
NSAssert( name, #"Invalid name");
in removeChildByName

Related

SpriteKit Removing sprites to redraw

Currently in my application I've been adopting a technique to remove/re-draw certain sprites.
in example, the app is a poker app. So when a call/raise/check is made, there is a chip that is placed in-front of the player with an SKLabelNode containing the bet,check etc.. However, removing the previous to then re-add the new is inconsistent and causes a lot of EXC_BAD_ACCESS errors. Now, I guess I could program it to nest search for that node and alter the value instead of redrawing. However, it's used in multiple occasions and will at some point rely on removing the child from it's parent.
What I'm asking is what is the best technique to achieve this without the possibility of inconsistent crashes..?
-(void)removePreviousChips:(NSString *)player {
NSString *string = [NSString stringWithFormat:#"chipLabel:%#", player];
SKNode *node = [tableSprite childNodeWithName:string];
[node removeFromParent];
}
-(void)updateChipStacksAndPlayerPositionsWith:(NSDictionary *)dict {
// [tableSprite removeAllChildren];
for (int i = 0; i < 8; i++) {
//loop through seats taken if value then draw.
if (![[dict valueForKey:_seatNames[i]] isEqualToString:#""]) {
[self drawAvatarWithPlayerName:[dict valueForKey:_seatNames[i]] withSeatPosition:[[seatPositions valueForKey:_seatNames[i]] CGPointValue] withChipStack:[[dict valueForKey:#"startingChips"] intValue]];
}
}
if ([self displayStartGameButton]) {
[startGameBG runAction:[SKAction fadeAlphaTo:1 duration:0.5]];
} else {
[startGameBG runAction:[SKAction fadeAlphaTo:0 duration:0.5]];
}
}
Them two examples are consistent ways to crash my app.
EDIT
for example, a better approach would be to detect whether the node is present before the requirement to remove it from it's parent and redraw it. However, something to detect it's presence is not working out for me
SKNode *node = [tableSprite childNodeWithName:[dict valueForKey:_seatNames[i]]];
if (node) {
NSLog(#"node for %# exists", [dict valueForKey:_seatNames[i]]);
} else {
NSLog(#"node for %# doesn't exist", [dict valueForKey:_seatNames[i]]);
}

How to do an on-item-changed for an NSPopUpButton?

I'm trying to implement a system that changes a label based on the state of an NSPopUpButton.
So far I've tried to do what's displayed in the code below, but whenever I run it, the code just jumps into the else clause, throwing an alert
- (IBAction)itemChanged:(id)sender {
if([typePopUp.stringValue isEqualToString: #"Price per character"]) {
_currency = [currencyField stringValue];
[additionalLabel setStringValue: _currency];
}
else if([typePopUp.stringValue isEqualToString: #"Percent saved"]) {
_currency = additionalLabel.stringValue = #"%";
}
else alert(#"Error", #"Please select a calculation type!");
}
So does anyone here know what to do to fix this?
#hamstergene is on the right track, but is comparing the title of the menu item rather than, say, the tag, which is wrong for the following reasons:
It means you cannot internationalize the app.
It introduces the possibility of spelling mistakes.
It's an inefficient comparison; comparing every character in a string takes way longer than comparing a single integer value.
Having said all that, NSPopUpButton makes it difficult to insert tags into the menu items, so you need to use the index of the selected item:
Assume you create the menu items using:
[typePopUp removeAllItems];
[typePopUp addItemsWithTitles: [NSArray arrayWithObjects: #"Choose one...", #"Price per character", #"Percent saved", nil]];
Then create an enum that matches the order of the titles in the array:
typedef enum {
ItemChooseOne,
ItemPricePerCharacter,
ItemPercentSaved
} ItemIndexes;
And then compare the selected item index, as follows:
- (IBAction)itemChanged:(id)sender {
NSInteger index = [(NSPopUpButton *)sender indexOfSelectedItem];
switch (index) {
case ItemChooseOne:
// something here
break;
case ItemPricePerCharacter:
_currency = [currencyField stringValue];
[additionalLabel setStringValue: _currency];
break;
case ItemPercentSaved:
_currency = #"%"; // See NOTE, below
additionalLabel.stringValue = #"%";
break;
default:
alert(#"Error", #"Please select a calculation type!");
}
}
NOTE the following line was incorrect in your code:
_currency = additionalLabel.stringValue = #"%";
Multiple assignment works because the result of x = y is y. This is not the case when a setter is involved. The corrected code is above.
EDIT This answer was heavily edited following more info from the OP.
To query the title of currently selected item in NSPopUpButton:
NSMenuItem* selectedItem = [typePopUp selectedItem];
NSString* selectedItemTitle = [selectedItem title];
if ([selectedItemTitle isEqualTo: ... ]) { ... }
Note that comparing UI strings is a very bad idea. A slightest change in UI will immediately break your code, and you are preventing future localization. You should assign numeric or object values to each item using -[NSMenuItem setTag:] or -[NSMenuItem setRepresentedObject:] and use them to identify items instead.

Check if mapView already contains an annotation

I have a method of adding secondary nearby annotations (ann2) when I tap on another annotation (ann1). But when I deselect and re-select the exact same annotation (ann1) the ann2 re-creates it self and is getting added again. Is there a way to check if the annotation already exists on the map and if yes then do nothing otherwise add the new annotation. I have already checked this: Restrict Duplicate Annotation on MapView but it did not help me.. Any advice is appreciated. This is what I have so far:
fixedLocationsPin *pin = [[fixedLocationsPin alloc] init];
pin.title = [NSString stringWithFormat:#"%#",nearestPlace];
pin.subtitle = pinSubtitle;
pin.coordinate = CLLocationCoordinate2DMake(newObject.lat, newObject.lon);
for (fixedLocationsPin *pins in mapView.annotations) {
if (MKMapRectContainsPoint(mapView.visibleMapRect, MKMapPointForCoordinate (pins.coordinate))) {
NSLog(#"already in map");
}else{
[mapView addAnnotation:pin];
}
In this case I get the log already on map but I also get the drop animation of the annotation adding to the map. Any ideas?
Thank you in advance..
Your for loop isn't checking if the annotation is on the screen, it is checking if the coordinates of the pin are currently within the visible area. Even if it was checking if the pin object was already in the mapView.annotations it would never be true, because you've only just created pin a few lines earlier, it can't possibly be the same object as on in the mapView.annotations. It might though have the same coordinates and title, and that's what you need to check:
bool found = false;
for (fixedLocationsPin *existingPin in mapView.annotations)
{
if (([existingPin.title isEqualToString:pin.title] &&
(existingPin.coordinate.latitude == pin.coordinate.latitude)
(existingPin.coordinate.longitude == pin.coordinate.longitude))
{
NSLog(#"already in map");
found = true;
break;
}
}
if (!found)
{
[mapView addAnnotation:pin];
}
Annotations array exist in map object so you just have to check
if ( yourmap.annotations.count==0)
{
NSLog(#"no annotations");
}
NSNumber *latCord = [row valueForKey:#"latitude"];
NSNumber *longCord = [row valueForKey:#"longitude"];
NSString *title = [row valueForKey:#"name"];
CLLocationCoordinate2D coord;
coord.latitude = latCord.doubleValue;
coord.longitude = longCord.doubleValue;
MapAnnotation *annotation = [[MapAnnotation alloc]initWithCoordinate:coord withTitle:title];
if([mkMapView.annotations containsObject:annotation]==YES){
//add codes here if the map contains the annotation.
}else {
//add codes here if the annotation does not exist in the map.
}
if (sampleMapView.annotations.count > 0) {
sampleMapView.removeAnnotation(detailMapView.annotations.last!)
}
Following my comment on Craig's answer, I think the solution could look like something like this :
import MapKit
extension MKMapView {
func containsAnnotation(annotation: MKAnnotation) -> Bool {
if let existingAnnotations = self.annotations as? [MKAnnotation] {
for existingAnnotation in existingAnnotations {
if existingAnnotation.title == annotation.title
&& existingAnnotation.coordinate.latitude == annotation.coordinate.latitude
&& existingAnnotation.coordinate.longitude == annotation.coordinate.longitude {
return true
}
}
}
return false
}
}
This code allows you to check if a mapView contains a given annotation. Use this in a "for" loop on all your annotations:
for annotation in annotations {
if mapView.containsAnnotation(annotation) {
// do nothing
} else {
mapView.addAnnotation(annotation)
}
PS: this works well if you need to add new annotations to a mapView. But if you need also to remove entries, you may have to do the opposite: check that each existing annotation exists in the new array of annotations ; if not, remove it.
Or you could remove everything and add everything again (but then you will have the change animated ...)

Dynamically created textfield validation

I'm trying to validate dynamically created text fields. The total number of textfields may vary.
The idea is to populate the empty fields with string like player 1, player 2 etc.. Here is what I try
-(IBAction)validateTextFields:sender
{
self.howManyPlayers = 3;
int emptyFieldCounter = 1;
NSMutableArray *playersNames = [NSMutableArray arrayWithCapacity:self.howManyPlayers];
while (self.howManyPlayers > 1)
{
self.howManyPlayers--;
UITextField *tmp = (UITextField *) [self.view viewWithTag:self.howManyPlayers];
if (tmp.text == nil)
{
[tmp setText:[NSString stringWithFormat:#"Player %d", emptyFieldCounter]];
emptyFieldCounter++;
}
[playersNames addObject:tmp.text];
}
}
The problems is that if I touch the button which invoke validateTextFields method. The first and the second textfield are populated with text Player 1 and Player 2, but the third field is not populated.
I notice also that if I type a text let's say in the second field touch the button then remove the text and again touch the button that field is not populated with text Player X.
How to make all that things to work correctly ?
change your code for two lines like this:
while (self.howManyPlayers >= 1) //edited line
{
UITextField *tmp = (UITextField *) [self.view viewWithTag:self.howManyPlayers];
if (tmp.text == nil)
{
[tmp setText:[NSString stringWithFormat:#"Player %d", emptyFieldCounter]];
emptyFieldCounter++;
}
[playersNames addObject:tmp.text];
self.howManyPlayers--; // moved line
}
I forgot ur second question, so edited my answer.
For that try with this. Change if (tmp.text == nil) with if (tmp.text == nil || [tmp.txt isEqualToString:#""])
The reason only two fields are populated is that you are only going through the while loop twice. It should be
while (self.howManyPlayers >= 1)
You should also move the decrement to the end of your while loop
while (self.howManyPlayers >= 1)
{
// other code here
self.howManyPlayers--;
}
For the second part of your question, I think when you delete the text from the control, it stops being nil and now becomes an empty string. So you need to check for an empty string as well as nil in your code.
if (tmp.text == nil || [tmp.txt isEqualToString:#""])

For loop variable always evaluating to true

Annoying newbie problem here. This variable isPlayerTouchingAnotherPlayer is being set to true as soon as I touch the piece. I'm almost positive I know why but I can't find a way to display this in log to confirm. I could probably do it by setting different flag numbers for each of the objects but I was hoping there is another way.
The problem is that piece is an object that is also located in p1Array so as soon as I touch it it hits itself and isPlayerTouchingAnotherPlayer is evaluated to true.
Is there a way I could print out the view or image name of the objects touching somehow to confirm this? And furthermore, is there a way to somehow avoid this annoying conflict.
for (int i = 0; i <[p1Array count]; i++) {
UIImageView *tempP1;
tempP1 =[p1Array objectAtIndex:i];
if (CGRectIntersectsRect(piece.frame, tempP1.frame)) {
NSLog(#"selected piece: %#, touched piece: %# ", piece, tempP1);
isPlayerTouchingAnotherPlayer = TRUE;
}
}
why not use fast enumeration and skip the image view you are not interested in checking.
for (UIImageView *imageView in p1Array) {
if (imageView == piece)
continue;
if (CGRectIntersectsRect(imageView.frame, piece.frame)) {
// do whatever
}
}
it seems like you are already printing out the names of the objects touching in the code sample you have provided. if you want to print out specific properties of the objects you can do that to.
as soon as i touch it it hits itself and isPlayerTouchingAnotherPlayer
is evaluated to true.
Then you should get a log message that shows the same object for the selected piece and the touched piece. If that's what's happening, then just add a condition to your if to prevent it:
if (piece != tempP1 && CGRectIntersectsRect(piece.frame, tempP1.frame)) {
Simply
NSLog(#"%# %#", piece, tempP1);
or
NSLog(#"%ld %ld", (NSInteger) piece, (NSInteger) tempP1);
The first will show you the description of the object, the second the address in memory, if it's same address, it's the same object.
You can simply check if it's the same object (pointer) with a simple equal check to exclude the same object :
if (piece != tempP1) {
if (CGRectIntersectsRect(piece.frame, tempP1.frame)) {
...
}
Furthermore, you would like to write this:
for ( int i = 0;
( ( i < [p1Array count] )
&& ( ! isPlayerTouchingAnotherPlayer )
);
i++
) {
...
}