Sound button not working? COCOS2D - objective-c

I tried this code it didn't work, I want the button to play sound when it is clicked.
-(void)starButtonsound:(id)sender
{
[[SimpleAudioEngine sharedEngine] playEffect:#"fire_truck_1.wav"];
}
In init
starMenuItemsound = [CCMenuItemImage
itemWithNormalImage:#"play.png" selectedImage:#"play.png"
target:self selector:#selector(starButtonsound:)];
starMenuItemsound.position = ccp(525, 500);
[self addChild:starMenuItemsound z: 4];
starMenuItemsound.scale = 0.75;
Whats wrong?

in init
{
// Using SpriteSheet ? Use spriteWithSpriteFrameName in place of spriteWithFile
CCSprite *spriteNormal = [CCSprite spriteWithFile:#"play.png"];
CCSprite *spriteSelected = [CCSprite spriteWithFile:#"play.png"];
ccColor3B color = {128,128,128};
spriteSelected.color = color;
starMenuItemsound = [CCMenuItemImage
itemWithNormalSprite: spriteNormal selectedImage: spriteSelected
target:self selector:#selector(starButtonsound:)];
starMenuItemsound.position = ccp(525, 500);
starMenuItemsound.scale = 0.75;
CCMenu *menu = [CCMenu menuWithItems: starMenuItemsound, nil];
menu.position = ccp(0.0f,0.0f);
[self addChild: menu z:4];
}
Call Function:
-(void) starButtonsound:(id)sender
{
[[SimpleAudioEngine sharedEngine] setEffectsVolume:1.0f]; //Do this in init method
[[SimpleAudioEngine sharedEngine] playEffect:#"fire_truck_1.wav"];
}

A) Set a breakpoint - essential knowledge for any programmer.
B) The CCMenuItemImage selector does not take an argument. Remove the colon from #selector(startButtonSound) and change the method to this:
-(void)starButtonsound
{
[[SimpleAudioEngine sharedEngine] playEffect:#"fire_truck_1.wav"];
}

Some reported that this is a solution: turning sound off and on again (no joke)
[SimpleAudioEngine sharedEngine].muted = YES;
[SimpleAudioEngine sharedEngine].muted = NO;

Related

UIRefreshControl incorrect title offset during first run and sometimes title missing

The text is offset wrong by the first launch of UIRefreshControl... later sometimes the refresh text doesn't show up at all and just the spiny is visible
I don't think i had this issue with iOS6... might be related to iOS7
Is in a UITableViewController added as a child to a VC, which resides in a modal presented UINavigationController
- (void)viewDidLoad {
[super viewDidLoad];
[self setRefreshControlText:#"Getting registration data"];
[self.refreshControl beginRefreshing];
}
- (void)setRefreshControlText:(NSString *)text {
UIFont * font = [UIFont fontWithName:#"Helvetica-Light" size:10.0];
NSDictionary *attributes = #{NSFontAttributeName:font, NSForegroundColorAttributeName : [UIColor blackColor]};
self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:text attributes:attributes];
}
This is definitely an iOS 7 bug, but I haven't figured out exactly what caused it. It appears to have something to do with the view hierarchy — adding my UITableViewController as a child view to a wrapper view controller appeared to fix it for me at first, although the bug is back since iOS 7 GM.
It looks like adding the following code to your UITableViewController after creating the refresh view fixes the positioning issue for good:
dispatch_async(dispatch_get_main_queue(), ^{
[self.refreshControl beginRefreshing];
[self.refreshControl endRefreshing];
});
calling endRefreshing under viewWillAppear did it for me:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.refreshControl endRefreshing];
}
Under iOS7 with a custom UITableViewController inside a UINavigationController
I had the same problem and for me it worked with layoutIfNeeded after setting the attributedTitle:
- (void)setRefreshControlText:(NSString *)text
{
UIColor *fg = [UIColor colorWithWhite:0.4 alpha:1.0];
NSDictionary *attrsDictionary = #{NSForegroundColorAttributeName: fg};
self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:text attributes:attrsDictionary];
[self.refreshControl layoutIfNeeded];
}
Cédric suggested to use [self.refreshControl setNeedsLayout], but this does not force an immediate update of the view, so you must use layoutIfNeeded.
I finally found the holy grail on this, which looks working in all cases
note : UIRefreshControl is added to a UITableViewController (note, never add UIRefreshControl just as subview to a normal UIVIewController's UITableView) (best to add UITableViewController as a child VC inside a UIViewController if you must)
note : that this also fixes the problem, that the UIRefreshControl is not vissible at first refresh (link)
Add to you .h
#interface MyViewController ()
#property (nonatomic, assign) BOOL refreshControlFixApplied;
- (void)beginRefreshing;
- (void)beginRefreshingWithText:(NSString *)text;
- (void)endRefreshing;
- (void)endRefreshingWithText:(NSString *)text;
#end
Add to you .m
////////////////////////////////////////////////////////////////////////
#pragma mark - UIRefreshControl Fix (peter#min60.com) https://stackoverflow.com/questions/19121276/uirefreshcontrol-incorrect-title-offset-during-first-run-and-sometimes-title-mis/
////////////////////////////////////////////////////////////////////////
- (void)beginRefreshingWithText:(NSString *)text {
[self setRefreshControlText:text];
[self beginRefreshing];
}
- (void)endRefreshingWithText:(NSString *)text {
[self setRefreshControlText:text];
[self.refreshControl endRefreshing];
}
- (void)beginRefreshing {
if (self.refreshControl == nil) {
return;
}
if (!self.refreshControlFixApplied) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.refreshControl.attributedTitle length] == 0) {
[self setRefreshControlText:#" "];
}
[self.refreshControl beginRefreshing];
dispatch_async(dispatch_get_main_queue(), ^{
[self.refreshControl endRefreshing];
dispatch_async(dispatch_get_main_queue(), ^{
// set the title before calling beginRefreshing
if ([self.refreshControl.attributedTitle length] == 0) {
[self setRefreshControlText:#" "];
}
if (self.tableView.contentOffset.y == 0) {
self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);
}
[self.refreshControl beginRefreshing];
self.refreshControlFixApplied = YES;
});
});
});
} else {
if (self.tableView.contentOffset.y == 0) {
self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);
}
[self.refreshControl beginRefreshing];
}
}
- (void)endRefreshing {
if (self.refreshControl == nil) {
return;
}
if (!self.refreshControlFixApplied) {
dispatch_async(dispatch_get_main_queue(), ^{
[self endRefreshing];
});
} else {
if (self.tableView.contentOffset.y < 0) {
self.tableView.contentOffset = CGPointMake(0, 0);
}
[self.refreshControl endRefreshing];
}
}
- (void)setRefreshControlText:(NSString *)text {
UIFont * font = [UIFont fontWithName:#"Helvetica-Light" size:10.0];
NSDictionary *attributes = #{NSFontAttributeName : font, NSForegroundColorAttributeName : [UIColor colorWithHex:0x00B92E]};
self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:text attributes:attributes];
}
Use only methods
- (void)beginRefreshing;
- (void)beginRefreshingWithText:(NSString *)text;
- (void)endRefreshing;
- (void)endRefreshingWithText:(NSString *)text;
UIRefreshControl seems to still be broken on IOS9.3 when you change the attributedTitle while the tableView is pulled down. What seems to work is to subclass UIRefreshControl and force update its layout once the (attributed) title is changed.
The core fix is to trigger a change to the tableView contentOffset (causing some hidden magic in the _update method which layouts the spinner and text subviews) and additionally forcing the frame height to its expected value ensuring the background color fills up the pulled down region.
#implementation MEIRefreshControl
{
__weak UITableView* _tableView;
}
- (instancetype)initWithTableView:(UITableView*)tableView
{
self = [super initWithFrame:CGRectZero];
if (self)
{
_tableView = tableView;
}
return self;
}
#synthesize title = _title;
- (void)setTitle:(NSString *)title
{
if (!PWEqualObjects(_title, title))
{
_title = title;
self.attributedTitle = [[NSAttributedString alloc] initWithString:_title ? _title : #""];
[self forceUpdateLayout];
}
}
- (void)forceUpdateLayout
{
CGPoint contentOffset = _tableView.contentOffset;
_tableView.contentOffset = CGPointZero;
_tableView.contentOffset = contentOffset;
CGRect frame = self.frame;
frame.size.height = -contentOffset.y;
self.frame = frame;
}
#end
This is the code that seems to fix all the issues. Many of the others that involved beginning or ending refreshing where interfering with other parts of the control.
//This chunk of code is needed to fix an iOS 7 bug with UIRefreshControls
static BOOL refreshLoadedOnce = NO;
if (!refreshLoadedOnce) {
__weak typeof(self) weakself = self;
[UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
self.tableView.contentOffset = CGPointMake(0, -weakself.refreshControl.frame.size.height);
} completion:^(BOOL finished) {
weakself.refreshControl.attributedTitle = self.refreshControl.attributedTitle;
[weakself.refreshControl setNeedsUpdateConstraints];
[weakself.refreshControl setNeedsLayout];
refreshLoadedOnce = YES;
}];
}
//End of bug fix
I had the same problem, I did solve it by setting attributed text with space string to refresh control directly after init refresh control
_refreshControl = [[UIRefreshControl alloc]init];
[_refreshControl setAttributedTitle:[[NSAttributedString alloc]initWithString:#" "]];
After that, setting new attributed text to refresh control was without any problems.
[[self refreshControl] setAttributedTitle:[[NSAttributedString alloc]initWithString:[NSString stringWithFormat:#"Последнее обновление: %#", [dateFormat stringFromDate:[_post dateUpdated]]]]];
UPDATE
I noticed that problem come back when I use attrsDictionary:
this code works fine
NSAttributedString* attributedString = [[NSAttributedString alloc]initWithString:string];
[[self refreshControl] setAttributedTitle: attributedString];
and this make refreshControl's title appear directly after view loaded
NSAttributedString* attributedString = [[NSAttributedString alloc]initWithString:string attributes:attrsDictionary];
[[self refreshControl] setAttributedTitle: attributedString];
I didn't find solution yet.
UPDATE
Finally found solution, after refreshcontrol init set attributed string also with attributes:attrsDictionary
NSDictionary *attrsDictionary = [NSDictionary dictionaryWithObjects:
[NSArray arrayWithObjects:[UIColor appDarkGray], [UIFont fontWithName:#"OpenSans-CondensedLight" size:14.0f], nil] forKeys:
[NSArray arrayWithObjects:NSForegroundColorAttributeName, NSFontAttributeName, nil]];
[_refreshControl setAttributedTitle:[[NSAttributedString alloc]initWithString:#" " attributes:attrsDictionary]];
so after that there is no problem to set new refreshcontrol's title.
The solution for me was to set a text in viewDidAppear, no need to call
beginRefreshing or endRefreshing on the mainQueue
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"d MMM, HH:mm"];
NSString *lastUpdated = [NSString stringWithFormat:NSLocalizedString(#"refresh_last_updated", nil),[formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:[[[DatabaseController sharedInstance] getCurrentSettings].lastTimeStamp doubleValue]]]];
UIFont *font = [UIFont fontWithName:FONT_LATO_LIGHT size:12.0f];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:lastUpdated attributes:#{NSFontAttributeName:font}];
_refreshControl.attributedTitle = attrString;
}

Disable CCMenuItem until animation finished

I have an CCMenuItem and I want to disable it until an animation is finished, but I don't want to disable all touches with:
[CCDirector sharedDirector].touchDispatcher.dispatchEvents = NO;
Any easy solution?
Use CCSequence to first use the animation action and when that is done you create an action that calls a function with the CCMenuItem as parameter and in that function make it enabled.
Code example: (PS: It was a long time since I used cocos2d.)
{
...
CCMenuItem *menuItem = [CCMenuItem itemWith...];
menuItem.isEnable = NO;
CCMenu *menu = [CCMenu menuWithItems:menuItem, nil];
[self addChild:menu];
[menuItem runAction:[CCSequence actions:[CCAction actionWith...], [CCCallFunc actionWithTarget:self selector:#selector(enable:)], nil]];
...
}
-(void)enable:(CCMenuItem *)item {
item.isEnable = YES;
}
You can disable the CCMenuItem with
CCMenuItem *item = [[CCMenuItem alloc] initWith...];
item.isEnabled = YES;
and enable it afterwards.

CDVViewController still executing JS after parent has been removed

I have attempted to insert a Cordova CleaverView (CDVViewController) into a UIViewController(SenchaViewController) instantiated from a storyboard in order to render a javascript Sencha based view.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (![ self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
self.view.layer.shadowOpacity = 0.75f;
self.view.layer.shadowRadius = 10.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
CDVViewController *cdvc = [CDVViewController new];
cdvc.wwwFolderName = #"www";
cdvc.startPage = #"sencha.html";
cdvc.useSplashScreen = NO;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
cdvc.view.frame = CGRectMake(0, 0, 768, 1004);
} else {
cdvc.view.frame = CGRectMake(0, 0, 320, 460);
}
[self.view addSubview:cdvc.view];
[self.view bringSubviewToFront:self.menuButton];
[self.view bringSubviewToFront:self.favButton];
cdvc = nil;
NSString *jsSetIdString = [NSString stringWithFormat:#"fetchGuid = function(){return '%#';}", [player valueForKey:#"playerID"]];
[[cdvc webView] stringByEvaluatingJavaScriptFromString:jsSetIdString];
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
}
My problem is after I remove the parent ViewController(SenchaViewController) which owns the CDVViewController's view instance, the CDVViewController does not get released and is still running in the background as I can still see javascript logging in the console. The causes for me to have several CDVViewController all running at once.
This is how the app adds the Parent View Controller:
- (void)renderPlayer:(NSNotification*)notification {
SenchaViewController* newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"SenchaView"];
NSDictionary *player = notification.object;
// Set active player
if( player != nil && [player valueForKey:#"playerID"] != #"" ){
// Attach Player Dictionary to Sencha View
newTopViewController.player = player;
// Send it to the top
CGRect frame = self.slidingViewController.topViewController.view.frame;
self.slidingViewController.topViewController = newTopViewController;
self.slidingViewController.topViewController.view.frame = frame;
}
This is how the Parent View controller which owns the CDVController is being removed
_topViewController.view removeFromSuperview];
[_topViewController willMoveToParentViewController:nil];
[_topViewController removeFromParentViewController];
Am I missing something? I was assuming ARC would dealloc this for me
Cordova has some dirty aspects, and this is one of them. Removing a CDVViewController is not enough to get it released. You have to also call the [controller dispose] method.

Core animation animationDidStop with chained animations

I have two animations in a custom UIView, anim1 and anim2. Anim1 sets its delegate to self and there is an animationDidStop method in my class which triggers Anim2. If I want something else to occur when Anim2 finishes, how do I do this? Can I specify a delegate method with a different name?
UPDATE
I declare two animations as iVars:
CABasicAnimation *topFlip;
CABasicAnimation *bottomFlip;
I build each animation and set delgate to self e.g.
- (CABasicAnimation *)bottomCharFlap: (CALayer *)charLayer
{
bottomFlip = [CABasicAnimation animationWithKeyPath:#"transform"];
charLayer.transform = CATransform3DMakeRotation(DegreesToRadians(0), 1, 0, 0); //set to end pos before animation
bottomFlip.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(DegreesToRadians(-360), 1, 0, 0)];
bottomFlip.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(DegreesToRadians(-270), 1, 0, 0)];
bottomFlip.autoreverses = NO;
bottomFlip.duration = 0.5f;
bottomFlip.repeatCount = 1;
bottomFlip.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];
bottomFlip.delegate = self;
bottomFlip.removedOnCompletion = FALSE;
return bottomFlip;
}
I then try and find bottomFlip in animationdidStop:
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
if (theAnimation == bottomFlip) {
NSLog(#"Bottom Animation is: %#", bottomFlip);
}
NSLog(#"Animation %# stopped",theAnimation);
[bottomHalfCharLayerFront addAnimation:[self bottomCharFlap:bottomHalfCharLayerFront] forKey:#"bottomCharAnim"];
bottomHalfCharLayerFront.hidden = NO;
topHalfCharLayerFront.hidden = YES;
//insert the next one???
}
"Animation stopped" is logged but nothing else i.e. it doesn't seem to recognise the bottomFlip iVar
This seems to work:
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
//NSLog(#"First Animation stopped");
if (anim ==[topHalfCharLayerFront animationForKey:#"topCharAnim"]) {
NSLog(#"Top Animation is: %#", anim);
topHalfCharLayerFront.hidden = YES;
[bottomHalfCharLayerFront addAnimation:[self bottomCharFlap:bottomHalfCharLayerFront] forKey:#"bottomCharAnim"];
bottomHalfCharLayerFront.hidden = NO;
}
else if ((anim ==[bottomHalfCharLayerFront animationForKey:#"bottomCharAnim"])) {
NSLog(#"Bottom Animation is: %#", anim);
}
Just hold onto a reference to your animations as ivars and compare their memory addresses to the one that gets handed off to animationDidStop:
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
if (theAnimation == anim1)
{
// Spin off anim2
}
else
{
// anim2 stopped. Make something else occur
}
}

Cocoa Touch - Looping Help

I need help with some programming logic... I need to loop this method to display the math problem in my labels then sleep(5) and then loop again. Anything I've tried ends of freezing the program. PLEASE help! I've been tearing my hair out trying everything I know!
EDIT: I edited the code to this, After 3 seconds it fades away the label displaying the problem but then it crashed and the debugger displays 2010-08-06 10:43:27.776 Reactor [13444:207] modifying layer that is being finalized - 0x5c13500
//quickfire problems
-(void)simpleMath{ //"Tap when the answer is X"
isTimeToTap = NO;
int problemSelector = arc4random() % 4;
NSArray *mathProblems = [[NSArray alloc] initWithObjects:#"6 - 1",#"2 + 3",#"3 x 2",#"3 x 1",#"2 x 4",nil]; //correct index 2
NSArray *mathAnswers = [[NSArray alloc] initWithObjects:#"5",#"5",#"6",#"3",#"8",nil]; //correct index 2
if ([mathAnswers objectAtIndex:problemSelector] == #"6") {
isTimeToTap = YES;
}
if (ranBefore == NO) { //create labels
//tell when to tap
tapTellerTop.text = #"Tap when the answer is 6!";
tapTellerBottom.text = #"Tap when the answer is 6!";
//make bottom label
mathDisplayBottom = [[UILabel alloc] initWithFrame:CGRectMake(15, 250, 242, 92)];
mathDisplayBottom.font = [UIFont fontWithName:#"Helvetica" size: 96.0];
mathDisplayBottom.textColor = [UIColor whiteColor];
mathDisplayBottom.backgroundColor = [UIColor clearColor];
[self.view addSubview: mathDisplayBottom];
//make top label
mathDisplayTop = [[UILabel alloc] initWithFrame:CGRectMake(55, 120, 242, 92)];
mathDisplayTop.font = [UIFont fontWithName:#"Helvetica" size: 96.0];
mathDisplayTop.textColor = [UIColor whiteColor];
mathDisplayTop.backgroundColor = [UIColor clearColor];
[self.view addSubview: mathDisplayTop];
//rotate top label
mathDisplayTop.transform = CGAffineTransformMakeRotation(180.0 /180.0 * M_PI);
}
//if ran before just update the text
mathDisplayBottom.text = [mathProblems objectAtIndex:problemSelector];
mathDisplayTop.text = [mathProblems objectAtIndex:problemSelector];
ranBefore = YES; //if its here. its been ran.
//run timer wait for (3) then loop again until userTaps = YES
[self performSelector:#selector(simpleMath) withObject:nil afterDelay:3.0f];
[mathProblems release];
[mathAnswers release];
[mathDisplayBottom release];
[mathDisplayTop release];
}
Sleep will stop your application from running or responding at all. Here is a quick partial example of a view controller you could use. This assumes you wire up the tap button and only uses one of the labels, etc. but you can play around with it. Also, this won't deal with memory issues or anything, so you'd add support for that.
But, once this view controller is created and the view installed, the math problem will update every 5 seconds. If the user presses the button and the answer is valid, we log a success message, otherwise we log fail message.
#interface myMathController : UIViewController {
NSArray* mathProblems;
NSIndexSet* validIndexes;
NSUInteger currentIndex;
NSTimer* timer;
}
#property(nonatomic,assign) IBOutlet UILabel* mathDisplayLabel;
- (void)updateProblem:(NSTimer*)timer;
- (IBAction)userTap;
#end
#implementation myMathController
- (void)viewDidLoad
{
[super viewDidLoad];
mathProblems = [[NSArray alloc] initWithObjects:#"6 - 1",#"2 + 3",#"3 x 2",#"3 x 1",#"2 x 4",nil];
// Add all valid answers
NSMutableIndexSet* tempIndexes = [[NSMutableIndexSet alloc] init];
[tempIndexes addIndex:2];
validIndexs = [tempIndexes copy];
[tempIndexes release];
timer = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:5.0 target:self selector:#selector(updateProblem:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)updateProblem:(NSTimer*)timer
{
currentIndex = arc4random() % [mathProblems count];
[mathDisplayLabel setText:[mathProblems objectAtIndex:currentIndex]];
}
- (void)userTap
{
if( [validIndexes containsIndex:currentIndex] ) {
NSLog(#"This user is SMART!");
} else {
NSLog(#"This user needs more work!");
}
}
- (void)dealloc
{
[timer invalidate];
[timer release];
[mathProblems release];
[validIndexes release];
[super dealloc];
}
#end
You shouldn't sleep(), ever. To invoke a function repeatedly, call:
[NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(doStuff:)
userInfo:nil repeats:YES];
then define the function:
-(void)doStuff:(NSTimer*)timer
{
// stuff
if ( iAmDone ) [timer invalidate];
}
OR if you want to fire another call, after 5 seconds, you could call
if ( !iAmDone ) [self performSelector:#selector(simpleMath) afterDelay:5];
at the end of simpleMath.