RunAction,CCCallFuncN with multiple Variables - Cocos2D - xcode

Is there any way you could send 2 parameters with RunAction?
You see im trying to move a sprite with a label on top, and I have made separate functions for each. Similar to this.
[sprite runAction:
[CCSequence actions:actionMove, actionMoveDone, nil]];
id actionMoveDone = [CCCallFuncN actionWithTarget:self
selector:#selector(spriteLabelMoveFinished:)];
Now, I've got 2 questions,
1-Is there any way to send 2 or more parameters????
2-I was wondering if there's any way to save some memory and do both with one action?
- (void) spriteMoveFinished:(id)sender
{
CCLOG(#"Sprite move finished");
Sprites *sprite = (Sprites *)sender;
[self animateSprite:sprite];
}
- (void) animateSprite:(Sprites *)zprite
{
CCLOG(#"We're animating sprite"):
Sprites *sprite = nil;
sprite = zprite;
int actualDuration = sprite.speed; //property of sprite
// Create the actions
id actionMove = [CCMoveBy actionWithDuration:actualDuration
position:ccpMult(ccpNormalize(ccpSub(_player.position,sprite.position)), 10)];
id actionMoveDone = [CCCallFuncN actionWithTarget:self
selector:#selector(spriteMoveFinished:)];
[sprite runAction:
[CCSequence actions:actionMove, actionMoveDone, nil]];
}
- (void) spriteLabelMoveFinished:(CCLabelTTF *)sender
{
[self animateSpriteLabel:sender];
}
-(void)animateEnemyHP:(CCLabelTTF *)zpriteLabel
{
CCLabelTTF *spriteLabel = nil;
spriteLabel = zpriteLabel;
int actualDuration = spriteSpeed; //another property
id actionMove = [CCMoveBy actionWithDuration:actualDuration
position:ccpMult(ccpNormalize(ccpSub(_player.position,spriteLabel.position)), 10)];
id actionMoveDone = [CCCallFuncN actionWithTarget:self
selector:#selector(spriteLabelMoveFinished:)];
[spriteLabel runAction:
[CCSequence actions:actionMove, actionMoveDone, nil]];
}
Now, this 4 functions are kind of obvious.
1-Move Sprite if sprite ended moving, we move it again.
2-Move Label towards the same position with the same speed, if the label finishes moving, we move it again.
They both go to the same place.
Is there a way to mix this 4 functions into 2?
If so, how can I send 2 parameters when the action finishes?
Thanks for your help and your time, have a great day!

To send more than one parameter, instead CCCallFunc you can use CCCallBlock and call a block of code. Inside the block, you can call your method (selector) with any parameters you want.
A simple example:
CCAction *actionMoveDone = [CCCallBlock actionWithBlock:^()
{
[self spriteMoveFinished:param1 withParam2:param2 andParam3:param3];
}];
And a better way is use "self" as block parameter, to prevent memory allocation:
CCAction *actionMoveDone = [CCCallBlockN actionWithBlock:^(CCNode *myNode)
{
[(MyClass*)myNode spriteMoveFinished:param1 withParam2:param2 andParam3:param3];
}];

try to simplify, that will undoubtedly save 'memory' ... I normally do this with a class that extends CCNode, for example my SoldierMapLayout class does. In the SoldierMapLayout node, i put in the soldier stance animation (idle, walking right-left-up-down), a health bar, an optional label, a 'HP hit' animation when appropriate, an 'XP gained' animation when appropriate, a 'poison cloud' over the head, etc... and i move the node when the whole thing needs to move. A single animation, with a single call back on move completion.

Related

select a view by clicking osx 10.6

i created a image view
for(int i=0; i<pcount; i++)
{
int x = rand() % 350;
int y = rand() % 350;
NSRect rect = NSMakeRect((x+10),(y+10), 200, 200);
//NSImageView *imageView
imageView1 = [[NSImageView alloc]initWithFrame:rect];
[imageView1 setTag:i];
// imageView = [[NSImageView alloc]initWithFrame:rect];
// [imageView1 rotateByAngle:rand() % 150];
[imageView1 setImageScaling:NSScaleToFit];
[imageView1 canBecomeKeyView];
NSImage *theImage = [[NSImage alloc]initWithContentsOfURL:(NSURL*)[patharray objectAtIndex:(i)]];
[imageView1 setImage:theImage];
[[imageView1 cell] setHighlighted:YES];
[[layoutCustom view] addSubview:imageView1 positioned:NSWindowMovedEventType relativeTo:nil];}
now how can select each image view by mouse click ? thanks in advance.
I'm assuming here that you have your reasons for not using existing collection views. So from what I read in your code you have layoutCustom.view, which contains a bunch of NSImageViews. Here are two options:
In your layoutCustom object implement the mouseDown: (or mouseUp: or both). Take the event location convert it view coordinates and look for any subview for which CGRectContainsPoint(subview.frame, mouseDownPoint) return YES. You should select that view.
Subclass NSImageView and implement mouseDown: (or mouseUp: or both). On mouseDown: simply set a "selected" flag. Either the view can draw something itself when selected or the layoutCustom object can observe the property and draw the selection accordingly.
I would prefer option 1 because it simpler, requires fewer classes and fewer interactions between objects.
// Option 1 (in layoutCustom class)
- (void) mouseDown:(NSEvent*)theEvent {
CGPoint mouseDownPoint = [self convertPoint:theEvent.locationInWindow fromView:nil];
for (NSView *view in self.subviews) {
if (CGRectContainsPoint(view.frame, mouseDownPoint)) {
// Do something to remember the selection.
// Draw the selection in drawRect:
[self setNeedsDisplay:YES];
}
}
}
// Option 2 (in Custom subclass of NSImage)
- (void) mouseDown:(NSEvent*)theEvent {
self.selected = !self.selected;
}
// Option 2 (in layoutCustom class)
- (void) addSubview:(NSView*)view positioned:(NSWindowOrderingMode)place relativeTo:(NSView*)otherView {
[super addSubview:view positioned:place relativeTo:otherView];
[self startObservingSubview:view];
}
- (void) willRemoveSubview:(NSView*)view {
[self stopObservingSubview:view];
}
- (void) startObservingSubview:(NSView*)view {
// Register your KVO here
// You MUST implement observeValueForKeyPath:ofObject:change:context:
}
- (void) stopObservingSubview:(NSView*)view {
// Remove your KVO here
}
I've got a better idea: Instead of fighting with converting mouse clicks in a view to coordinates and then figuring out how to map it to the right subview or sub-image, why not have one big (or scrolling?) view and then add your images as giant "NSButton" objects (set to custom type), where the button images can be the images you want to add.
As for how to select each image? You can either subclass "NSButton" and keep track of some custom data within it, or you can use a "tag" to figure out which button was pressed in your "IBAction" method and then decide what to do with it.
Another approach might be to embed your images into NSTableView cells...

Button Crashes App with (id) sender

I am attempting to make a basic game which requires a serious of buttons to control player movement. Keep in mind I am using cocos-2d. My goal is to have the buttons be holdable and move a sprite when held down. The code i am using now looks like this.
CCMenuItemHoldable.h
#interface CCMenuItemSpriteHoldable : CCMenuItemSprite {
bool buttonHeld;
}
#property (readonly, nonatomic) bool buttonHeld;
CCMenuItemHoldable.m
#implementation CCMenuItemSpriteHoldable
#synthesize buttonHeld;
-(void) selected
{
[super selected];
buttonHeld = true;
[self setOpacity:128];
}
-(void) unselected
{
[super unselected];
buttonHeld = false;
[self setOpacity:64];
}
#end
and for the set up of the buttons
rightBtn = [CCMenuItemSpriteHoldable itemFromNormalSprite:[CCSprite spriteWithFile:#"art/hud/right.png"] selectedSprite:[CCSprite spriteWithFile:#"art/hud/right.png"] target:self selector:#selector(rightButtonPressed)];
CCMenu *directionalMenu = [CCMenu menuWithItems:leftBtn, rightBtn, nil];
[directionalMenu alignItemsHorizontallyWithPadding:0];
[directionalMenu setPosition:ccp(110,48)];
[self addChild:directionalMenu];
This all seems to work fine but when i do
-(void)rightButtonPressed:(id) sender
{
if([sender buttonHeld])
targetX = 10;
else{
targetX = 0;
}
}
The crash has been fixed but I am trying to get my sprite to move. In my game tick function I add the value of targetX to the position of the sprite on a timer, still no movement.
Please, always include a crash log when asking questions about crashes.
In your case, I can guess the problem. You are adding this selector:
#selector(rightButtonPressed)
Your method is called
rightButtonPressed:(id)sender
Which would be, as a selector, rightButtonPressed: - note the colon indicating that an argument is passed. Either change the method so it has no argument, or add a colon to the selector when you create the button.
The crash log would be telling you this - it would say "unrecognised selector sent to..." with the name of the receiving class, and the name of the selector.

How to add subview in ListView?

I am developing my first MAC application, i downloaded one Example of PxListView
and i have to added one button and background image on cell xib and bind them with controller
and, when on button click i was set height of that cell is much bigger then other. that is done,
and work fine.
but now i want to develop like after is witch cell has open in that cell i want to add some extra contain (Controller) on it, so how it will possible using given example?
pls help me to give some suggest how it will be done.
for Ex like before click on button
after chick on button i want to develop like
You write
i have to added one button and background image on cell xib and bind them with controller
It sounds like you've subclassed PXListViewCell--for convenience, let's call your subclass TemplateListViewCell--and added a xib from which instances of TemplateListViewCell will be loaded in
+[PXListViewCell cellLoadedFromNibNamed:bundle:reusableIdentifier:]
In addition, there is a[t least one] button in TemplateListViewCell.xib.
You write
when on button click i was set height of that cell is much bigger then other. that is done, and work fine
It sounds like this button has as its action a method on TemplateListViewCell such as
- (IBAction)toggleDetail:(id)sender
{
//Code to grow or shrink the height of [self frame].
//...
}
In my approach to implementing -toggleDetail, two modifications to the PXListView files were necessary:
1. Adding a protocol method
- (void)listView:(PXListView *)aListView setHeight:(CGFloat)height ofRow:(NSUInteger)row;
to the PXListViewDelegate protocol.
2. Adding a property
#property (nonatomic, assign) BOOL expanded;
to PXListViewCell.
My implementation of -toggleDetail looks something like this:
- (IBAction)toggleDetail:(id)sender
{
BOOL wasExpanded = [self expanded];
NSRect oldFrame = [self frame];
CGFloat oldHeight = oldFrame.size.height;
CGFloat newHeight = oldHeight;
CGFloat heightIncrement = 0.0f;
if (wasExpanded) {
heightIncrement = -80.0f; //use whatever value is appropriate
} else {
heightIncrement = 80.0f; //use whatever value is appropriate
}
newHeight += heightIncrement;
[[[self listView] delegate] listView:[self listView] setHeight:newHeight ofRow:[self row]];
[[self listView] reloadData];
BOOL isExpanded = !wasExpanded;
[self setExpanded:isExpanded];
}
It might seem better to use [[self listView] reloadRowAtIndex:[self row]]; in place of [[self listView] reloadData], but unfortunately, this doesn't work: if the user hides the detail--shrinks the cell vertically--new cells which should appear on the screen do not.
You write
that is done, and work fine.
It sounds like you were able to implement successfully a method analogous to -[TemplateListViewCell toggleDetail:].
You write
but now i want to develop like after is witch cell has open in that cell i want to add some extra contain (Controller) on it, so how it will possible using given example? pls help me to give some suggest how it will be done.
It sounds like you want instances of TemplateListViewCell to contain extra views if they are expanded.
It might seem tempting to put this code into -[TemplateListViewCell toggleDetail], but this will not work out as we might hope. The trouble is, we need to handle cases where expanded cells have been scrolled out of view and scrolled back into view.
To get this right, we need to have a notion of expanded which persists beyond the usage of a PXListViewCell subclass instance: we either need to keep track of expansion in the PXListView itself or in its delegate.
The better--but less expedient--design seems to be to keep track of this information in the PXListView itself. For the sake of this question, however, I'll demonstrate how to keep track of cell expansion in the delegate. To do this, I'm expanding the PXListViewDelegate protocol and making other changes to the PXListView files:
1. Adding the methods
- (void)listView:(PXListView *)aListView setExpanded:(BOOL)expanded atRow:(NSUInteger)row;
- (BOOL)listView:(PXListView *)aListView expandedAtRow:(NSUInteger)row;
to PXListViewDelegate.
2. Adding the method
- (void)setCell:(PXListViewCell *)cell expandedAtRow:(NSUInteger)row
{
if ([[self delegate] respondsToSelector:#selector(listView:expandedAtRow:)]) {
[cell setExpanded:[[self delegate] listView:self expandedAtRow:row]];
}
}
to PXListView.
3. Calling -[PXListView setCell:expandedAtRow:] from -[PXListView layoutCells]
- (void)layoutCells
{
//Set the frames of the cells
for(id cell in _visibleCells)
{
NSInteger row = [cell row];
[cell setFrame:[self rectOfRow:row]];
[self setCell:cell expandedAtRow:row];
[cell layoutSubviews];
}
NSRect bounds = [self bounds];
CGFloat documentHeight = _totalHeight>NSHeight(bounds)?_totalHeight:(NSHeight(bounds) -2);
//Set the new height of the document view
[[self documentView] setFrame:NSMakeRect(0.0f, 0.0f, NSWidth([self contentViewRect]), documentHeight)];
}
and from -[PXListView layoutCell:atRow:]:
- (void)layoutCell:(PXListViewCell*)cell atRow:(NSUInteger)row
{
[[self documentView] addSubview:cell];
[cell setFrame:[self rectOfRow:row]];
[cell setListView:self];
[cell setRow:row];
[cell setHidden:NO];
[self setCell:cell expandedAtRow:row];
}
4. Setting _expanded to NO in -[PXListViewCell prepareForReuse]:
- (void)prepareForReuse
{
_dropHighlight = PXListViewDropNowhere;
_expanded = NO;
}
Note: In the sample PXListViewCell subclass, MyListViewCell, distributed with PXListView, the implementation of -[MyListViewCell prepareForReuse] fails to call [super prepareForReuse]. Make sure that this call is made in [TemplateListViewCell prepareForReuse]:
- (void)prepareForReuse
{
//...
[super prepareForReuse];
}
One change needs to be made to -[TemplateListViewCell toggleDetail:]. The line
[self setExpanded:isExpanded];
needs to be replaced by
[[[self listView] delegate] listView:[self listView] setExpanded:isExpanded atRow:[self row]];
Once you've set up your PXListView's delegate to properly handle the new delegate methods, you're ready to override [PXListViewCell setExpanded:] in your subclass TemplateListViewCell:
- (void)setExpanded:(BOOL)expanded
{
if (expanded) {
//add detail subviews
} else {
//remove detail subviews
}
[super setExpanded:expanded];
}
Replace //add detail subviews with your own code which programmatically adds the detail subviews that you want and replace //remove detail subviews with code to remove the detail subviews that you want, checking to see that they are present first.
You write
i want to add some extra contain (Controller) on it
It sounds like you want to add view controllers rather than views to your TemplateListViewCell. To do this, use an NSBox and set the box's contentView to your view controller's view. (For details on this, see this answer.)
If you plan on just showing a single view controller's view in an NSBox on the expanded TemplateListViewCell, you can just (1) add a property to TemplateListViewCell referencing your view controller and (2) add an NSBox to TemplateListViewCell xib and set its contentView to the appropriate view controller's view on [cell setExpanded:YES] and set its contentView to nil on [cell setExpanded:NO].

Creating a reference to a UIImageView from a string

I have a series of UIImageViews which are created in IB. I want to show and hide these depending on some button presses. I currently have a method which says if 1 is pressed then hide 2,3,4 then if 2 is pressed then hide 1,3,4. This works but I am trying to improve my code for an update.
My background is actionscript so I'm not sure if what I am trying to do is the right thing.
I basically want to evaluate a string reference to UIImageView, in AS I would use eval(string).
The method I am using is to create a string from a string and a number, so I get "image1". All works, but then I need to evaluate this into a UIImageView so I can update the alpha value.
Firstly is this possible and if not how should I be doing this? I'm starting to think that having this set up in interface builder maybe isn't helping?
That's probably not a good way to work. What you want is an array of imageViews. Then you just need a numeric index, and you can go through the array of imageViews hiding everything that doesn't have the chosen index.
But how do you get an array of imageViews? See How to make IBOutlets out of an array of objects? It explains how to use IBOutletCollection.
If you have a separate button for each view, put those into an IBOutletCollection too. That way you can have something like this:
- (IBAction) imageButtonPressed:(id) sender;
{
// The sender is the button that was just pressed.
NSUInteger chosenIndex = [[self imageButtons] objectAtIndex:sender];
for (NSUInteger imageIndex = 0; imageIndex < [[self imageViews] count]; imageIndex++)
{
// Hide all views other than the one associated with the pressed button.
if (imageIndex != chosenIndex)
{
[[[self imageViews] objectAtIndex:imageIndex] setHidden:YES];
}
else
{
[[[self imageViews] objectAtIndex:imageIndex] setHidden:NO];
}
}
}
If you really, really need to associate the string image1 with an imageView, you can construct an NSDictionary associating your controls with unique string identifiers for later lookup. NSDictionary is powerful, but I'm drawing a blank on reasons why this would be needed.
NSMutableDictionary *viewLookup;
[viewLookup setObject:[[self imageViews] objectAtIndex:0] forKey:#"image0"];
[viewLookup setObject:[[self imageViews] objectAtIndex:1] forKey:#"image1"];
[viewLookup setObject:[[self imageViews] objectAtIndex:2] forKey:#"image2"];
[viewLookup setObject:[[self imageButtons] objectAtIndex:0] forKey:#"button0"];
// ...
// Can now look up views by name.
// ...
NSString *viewName = #"image1";
UIView *viewFound = [viewLookup objectForKey:viewName];
[viewFound doSomething];

CALayer: callback when animation ends?

I have been running into some issues with animating multiple CALayers at the same time, and was hoping someone could point me in the right direction.
My app contains an array of CALayer. The position of each layer is set to (previousLayer.position.y + previousLayer.bounds.height), which basically lays them out similar to a table. I then have a method that, every-time it is called, adds a new layer to the stack and sets its Y position is set to 0. The Y positions of all other layers in the array are then offset by the height of the new layer (essentially pushing all old layers down).
What I am having problems with is preventing the adding of new layers until the previous animation has completed. Is there a way to tell when an implicit animation has finished? Or alternatively, if I use CABasicAnimation and animationDidFinish, is there a way to tell which object finished animating when animationDidFinish is called?
You can set arbitrary values for keys on your animation object. What this means is that you can associate your layer that you're animating with the animation and then query it in -animationDidStop:finished: You create your animation this way:
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position"];
// set other fields...
[anim setValue:layerToAnimate forKey:#"layer"];
// Start the animation
[layerToAnimate addAnimation:anim forKey:nil];
Then check for that value when the animation stops:
- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag
{
CALayer *animatedLayer = [animation valueForKey:#"layer"];
// do something with the layer based on some condition...
// spin off the next animation...
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
forKey:kCATransactionDisableActions];
[animatedLayer setPosition:position];
[CATransaction commit];
}
This is explicit animation, but it should give you what you need.
You can try surrounding your code in a CATransaction. Here's how that would look in Swift 3:
CATransaction.begin()
CATransaction.setCompletionBlock({
// run after the animations
})
// animtations
CATransaction.commit()
It turns out that rather than adding the CABasicAnimation directly to the CALayer, I had to add it to the layer's 'actions' dictionary... This leaves the layer at it's final position after the animation ends, but still calls the 'animationDidFinish' method.
-(void)startAnimation {
if(!animating){
animating = YES;
for(int i=0; i<[tweets count]; i++) {
//get the layer
CETweetLayer *currentLayer = [tweets objectAtIndex:i];
//setup the orgin and target y coordinates
float targetY = currentLayer.position.y + offset;
//setup the animation
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position"];
anim.delegate = self;
currentLayer.actions = [NSDictionary dictionaryWithObject:anim forKey:#"position"];
currentLayer.position = CGPointMake(self.bounds.size.width/2, targetY);
}
}
}
And then...
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
animating = NO;
}

Resources