How to use CCActionProperty, Does it not exist in Cocos2d 3.0? - objective-c

I was looking up the different abilities of Cocos2d, and don't quite understand what property is being modified here:
id rot = [CCPropertyAction actionWithDuration:2 key:#"rotation" from:0 to:-270];
id rot_back = [rot reverse];
id rot_seq = [CCSequence actions:rot, rot_back, nil];
Specifically, the "rotation" of what sprite? Rotation is a property of CCSprite, but I see no CCSprite here, so I'm very confused.
Furthermore, it seems to have existed in an earlier version of Cocos2d, so what has happened to it?

I've never seen CCPropertyAction used... I'll show you how I would do what you exampled.
// Create rotate action
CCActionRotateBy *rotateAction = [CCActionRotateBy actionWithDuration:2.0f angle:-270];
// Create a copy of that action and reverse it
id reverseAction = [[rotateAction copy] reverse];
// Create a sequence of actions in order
CCActionSequence *sequenceAction = [CCActionSequence actions:rotateAction,reverseAction, nil];
// mySprite will run actions in order
[mySprite runAction:sequenceAction];
In previous versions of cocos2d the actions were CCRotateBy and CCSequence, they just changed names for the sake of confusion.
All cocos2d actions will have CCAction... before them to classify them as actions.

Related

How can I test for the existence of a UIView using XCUIElementsQuery?

It is difficult to find proper documentation for the UI Testing Framework from Apple. I've seen many examples where it is possible to find buttons and labels with specific names, but how can I look for generic UIViews?
For example, suppose I set a view with accessibilityLabel == "Some View", how can I find a view with that name during the test execution?
I tried (unsuccessfully) the following code:
XCUIElement *pvc = [[app childrenMatchingType:XCUIElementTypeAny] elementMatchingPredicate:[NSPredicate predicateWithFormat:#"accessibilityLabel == 'Places View'"]];
NSPredicate *exists = [NSPredicate predicateWithFormat:#"exists == true"];
[self expectationForPredicate:exists evaluatedWithObject:pvc handler:nil];
[self waitForExpectationsWithTimeout:10 handler:nil];
NSLog(#"%#", [[app childrenMatchingType:XCUIElementTypeAny] elementMatchingPredicate:[NSPredicate predicateWithFormat:#"accessibilityLabel == 'Places View Controller'"]]);
The problem I see with the code you have above is that you're specifically trying to find a view in the children of the app, instead of the descendants of the app. Children are strictly subviews of a view, and the search won't look at sub-sub-views, etc. Other than that, it looks fine.
Try:
XCUIElement *pvc = [[app descendantsMatchingType:XCUIElementTypeAny] elementMatchingPredicate:[NSPredicate predicateWithFormat:#"accessibilityLabel == 'Places View'"]];
Also, the default for UIView is to not participate in accessibility at all, so in addition to setting the label (and incidentally the code snippet in your question about setting a label is doing comparison instead of assignment) you should also make sure you enable accessibility:
view.isAccessibilityElement = YES
view.accessibilityLabel = #"AccessibilityLabel"
The query from the other answer works, but I find that you can also find views simply with:
app.otherElements["MenuViewController.menuView"].swipeLeft()
Where in my case MenuViewController.menuView is a UIView with an accessibilityIdentifier set.

CCActionDelay not working as expected

What I have is a method that creates CCSprites:
-(void)createDebrisAtPosition:(CGPoint)position{
NSInteger numberOfPieces = [random randomWithMin:5 max:20];
for (int i=0; i<numberOfPieces; i++) {
CCSprite *debris = [CCSprite spriteWithImageNamed:#"debri.png"];
debris.position = position;
debris.physicsBody = [CCPhysicsBody bodyWithRect:CGRectMake(0, 0, debris.contentSize.width, debris.contentSize.height) cornerRadius:0];
debris.physicsBody.collisionType = #"debris";
debris.name = #"Debris";
CCActionRemove *removeAction = [CCActionRemove action];
CCActionSequence *sequence = [CCActionSequence actions:[CCActionDelay actionWithDuration:2.0], removeAction, nil];
[physics addChild:debris];
//physics is a CCPhysicsNode here
[debris runAction:sequence];
}
}
This method then gets invoked during specific collision events:
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair enemy:(EnemyNode*)enemy projectile:(ProjectileNode*)projectile
{
[enemy removeFromParent];
[projectile removeFromParent];
[self createDebrisAtPosition:enemy.position];
return NO;
}
Expected behavior: CCSprites should appear and then get removed only after 2.0 secs.
Actual behavior: CCSprites appear for a split-second, then instantly get removed.
I also tried CCActionInterval, CCActionEaseOut, but they didn't work (And they shouldn't, according to the docs, but CCActionDelay — should, but not working). I changed the order of method invocation (runAction after and before addChild), as well as the order of action invocation this didn't work as well. Don't mind the CCActionDelay declaration directly in the CCActionSequence — I tried to declare it as a separate variable, with zero luck.
What am I misunderstanding here?
I'm new here so I'm not allowed to comment yet (this would perhaps be better suited as a comment) but: I haven't been able to recreate your problem. The problem is not related to CCActionDelay or the actions you are running on the debris sprite. You can test this yourself by running your sequence in a different setup. Ergo: there must be a problem somewhere else in your code. I'm sorry, but I cannot help any further based on the example code you've posted.

CCMenuItems loose tags

I'm adding two CCMenuItemImage objects to a CCMenuItemToggle like so:
CCMenuItemImage *soundEnabled = [CCMenuItemImage itemWithNormalImage:#"button_sound_enabled.png"
selectedImage:#"button_sound_enabled.png"];
soundEnabled.tag = kSoundEnabled;
CCMenuItemImage *soundDisabled = [CCMenuItemImage itemWithNormalImage:#"button_sound_disabled.png"
selectedImage:#"button_sound_disabled.png"];
soundDisabled .tag = kSoundDisabled;
CCMenuItemToggle *sound = [CCMenuItemToggle itemWithItems:[NSArray arrayWithObjects:soundEnabled,soundDisabled,nil] block:^(id sender) {
CCMenuItem *item= ((CCMenuItemToggle*).sender).selectedItem;
CCLog(#"item tag: %d",item.tag);
}];
kSoundEnabled and kSoundDisabled are enumeration items with the values 2 and 3. When I log the tag of each CCMenuItemImage after i created them, everything is fine. But when I log them inside the block, the tags show up as -1061138431.
Also when I try to log them outside of the block, just a but further down in the init code of my layer they start to turn up wrong.
Does anybody know what the issue is here? It's a Kobold2d ARC-enabled project, could ARC be the issue here? I thought this wouldn't account for simple datatypes like NSInteger?
I know I could just check for sender.selectedIndex = 0 or sender.selectedIndex = 1 but I'd still like to understand what's the issue here.
The issue does not come from the ARC configuration or others. I tested it with Cocos2D 2.0 and I have the same issue. I checked the sources and the problem comes from the CCMenuItemToggle which change the tag of the children to keep the track of the current display item.
I should you to use the reference of your variable into your block like that:
CCMenuItemToggle *sound = [CCMenuItemToggle itemWithItems:[NSArray arrayWithObjects:soundEnabled,soundDisabled,nil] block:^(id sender)
{
CCMenuItem *item= ((CCMenuItemToggle*).sender).selectedItem;
if (item == soundEnabled)
{
//...
} else
{
//...
}
}];

Performance issues using an MPMusicPlayerController - can it be accessed in the background?

EDIT: Updating my own question with the answer I figured out months later. Short answer is no, MPMusicPlayerController forwards all calls to the main thread. But it uses a CPDistributedMessagingCenter to actually handle all operations, so one can very easily write a replacement controller that makes asynchronous calls (but it won't work for a sandboxed App Store app, as far as I know - and if it did, Apple would promptly reject it).
I'm making a simple app to control iPod playback, so I've been using an MPMusicPlayerController, which Apple states can only be used in the main thread. However, I've been experiencing some frustrating performance issues in the UI. Switching to the next or previous song is triggered by a swipe, which moves the entire display (of the song info) with it, and then updates the display for the next song when the switch is triggered. The trouble is that once the song has been changed, the UI hangs for up to a second while the song info is retrieved from the MPMusicPlayerController. I've tried most everything I can think of to optimize the code, but it seems to me that the only way to fix it is to move the MPMusicPlayerController code on to a background thread, despite Apple's instructions not to.
The code to update the display, for reference:
// Called when MPMusicPlayerControllerNowPlayingItemDidChangeNotification is received
- (void) nowPlayingDidChange {
if ([iPodMusicPlayer nowPlayingItem]) {
// Temp variables (necessary when updating the subview values in the background)
title = [[iPodMusicPlayer nowPlayingItem] valueForProperty:MPMediaItemPropertyTitle];
artist = [[iPodMusicPlayer nowPlayingItem] valueForProperty:MPMediaItemPropertyArtist];
album = [[iPodMusicPlayer nowPlayingItem] valueForProperty:MPMediaItemPropertyAlbumTitle];
artwork = [[[iPodMusicPlayer nowPlayingItem] valueForProperty:MPMediaItemPropertyArtwork] imageWithSize:CGSizeMake(VIEW_HEIGHT - (2*MARGINS), VIEW_HEIGHT - (2*MARGINS))];
length = [[[iPodMusicPlayer nowPlayingItem] valueForProperty:MPMediaItemPropertyPlaybackDuration] doubleValue];
if (updateViewInBackground)
[self performSelectorInBackground:#selector(updateSongInfo) withObject:nil];
else
[self updateSongInfo];
}
else
[self setSongInfoAsDefault];
}
- (void) updateSongInfo {
// Subviews of the UIScrollView that has performance issues
songTitle.text = title;
songArtist.text = artist;
songAlbum.text = album;
songLength.text = [self formatSongLength:length];
if (!artwork) {
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)] && [[UIScreen mainScreen] scale] == 2.00)
songArtwork.image = [UIImage imageWithContentsOfFile:
#"/System/Library/Frameworks/MediaPlayer.framework/noartplaceholder#2x.png"];
else
songArtwork.image = [UIImage imageWithContentsOfFile:
#"/System/Library/Frameworks/MediaPlayer.framework/noartplaceholder.png"];
}
else
songArtwork.image = artwork;
title = nil;
artist = nil;
album = nil;
artwork = nil;
length = 0.0;
}
Is there anything I'm missing here (ie. performance optimization when updating the UIScrollView subviews)? And if not, would it be such a bad idea to just use the MPMusicPlayerController in a background thread? I know that can lead to issues if something else is accessing the iPodMusicPlayer (shared instance of the iPod in MPMusicPlayerController), but are there any ways I could potentially work around that?
Also, this is a jailbreak tweak (a Notification Center widget), so I can make use of Apple's private frameworks if they would work better than, say, the MPMusicPlayerController class (which is fairly limited anyways) for my purposes. That also means, though, that my app will be running as a part of the SpringBoard process, so I want to be sure that my code is as safe and stable as possible (I experience 2 minute hangs whenever my code does something wrong, which I don't want happening when I release this). So if you have any suggestions, I'd really appreciate it! I can provide more code / info if necessary. Thanks!

Check if an action is currently running?

Is it possible to check if there are actions currently running in a CCNode class in Cocos2d? I'd like to know if a CCMoveBy is still running or not.
You can use [self numberOfRunningActions] on any CCNode. In your case, it sounds like you want to know if there are simply any actions running or not, so it's not a big deal to know the exact number beforehand.
We can easily check if specific actions run by using getActionByTag method and action.tag property.
There is no need to introduce the CCCallFuncN callbacks or counting numberOfRunningActions.
Example.
In our app it is important to let the jumpAction to be finished prior to executing another jump. To prevent triggering another jump during an already running jump action
the critical jump section of code is protected as follows:
#define JUMP_ACTION_TAG 1001
-(void)jump {
// check if the action with tag JUMP_ACTION_TAG is running:
CCAction *action = [sprite getActionByTag:JUMP_ACTION_TAG];
if(!action) // if action is not running execute the section below:
{
// create jumpAction:
CCJumpBy *jumpAction = [CCJumpBy actionWithDuration:jumpDuration position:ccp(0,0) height:jumpHeight jumps:1];
// assign tag JUMP_ACTION_TAG to the jumpAction:
jumpAction.tag = JUMP_ACTION_TAG;
[sprite runAction:jumpAction]; // run the action
}
}
You can always add a method to indicate when the method is finished, and then toggle some BOOL or something like that to indicate it is not running, and put a start method to toggle the BOOL to indicate it started:
id actionMove = [CCMoveTo actionWithDuration:actualDuration
position:ccp(-target.contentSize.width/2, actualY)];
id actionMoveDone = [CCCallFuncN actionWithTarget:self
selector:#selector(spriteMoveFinished:)];
id actionMoveStarted = [CCCallFuncN actionWithTarget:self
selector:#selector(spriteMoveStarted:)];
[target runAction:[CCSequence actions:actionMoveStarted, actionMove, actionMoveDone, nil]];
Modified from here.
In the two #selector methods:
-(void) spriteMoveStarted:(id)sender {
ccMoveByIsRunning = YES;
}
and:
-(void) spriteMoveFinished:(id)sender {
ccMoveByIsRunning = NO;
}
where ccmoveByIsRunning is the BOOL I'm referring to.
EDIT: As xus has pointed out, you should actually not do this and instead use [self numberOfRunningActions] as others have pointed out.